From: Stefan Csomor Date: Mon, 28 Jul 2003 20:44:57 +0000 (+0000) Subject: metadata streaming X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/70e881039bfeab2f3cdd42ea8093f7313c5623a6?ds=inline metadata streaming git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@22359 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/xtistrm.h b/include/wx/xtistrm.h new file mode 100644 index 0000000000..3d7fa4c759 --- /dev/null +++ b/include/wx/xtistrm.h @@ -0,0 +1,183 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/xtistrm.h +// Purpose: streaming runtime metadata information (extended class info) +// Author: Stefan Csomor +// Modified by: +// Created: 27/07/03 +// RCS-ID: $Id$ +// Copyright: (c) 2003 Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_XTISTRMH__ +#define _WX_XTISTRMH__ + +#if defined(__GNUG__) && !defined(__APPLE__) +#pragma interface "xtistrm.h" +#endif + +#include "wx/wx.h" + +#if wxUSE_EXTENDED_RTTI + +// Filer contains the interfaces for streaming objects in and out of XML, +// rendering them either to objects in memory, or to code. Note: We +// consider the process of generating code to be one of *depersisting* the +// object from xml, *not* of persisting the object to code from an object +// in memory. This distincation can be confusing, and should be kept +// in mind when looking at the property streamers and callback interfaces +// listed below. + +/* + Main interface for streaming out an object to XML. +*/ + +void WriteComponent(wxObject *Object, const wxClassInfo *ClassInfo, wxXmlNode *parent, const wxString& nodeName ); + +class wxReader; +/* + Streaming callbacks for depersisting XML to code, or running objects +*/ + +struct wxIDepersist ; + +/* + wxReader handles streaming in a class from XML. Maintains a list of + objects, and names, and issues calls out to interfaces to depersist the + guts from the XML tree. +*/ +class wxReader : wxObject +{ + struct wxReaderInternal; + wxReaderInternal *Data; + + wxxVariant ReadPropertyValueNoAssign(wxXmlNode *Node, + wxClassInfo *ClassInfo, + const wxPropertyInfo *& propertyInfo , + wxIDepersist *Callbacks = NULL); + + void ReadPropertyValue(wxXmlNode *Node, + wxClassInfo *ClassInfo, + int ObjectId , + wxIDepersist *Callbacks = NULL ); + + bool genCode; // true if the reader should generate code. + // ISSUE: ick! + // this interface is getting crufty. Why the + // different depersist callbacks in here, if there + // is another place that knows that we're generating + // code? Needs some repair work. +public: + wxReader(bool GenerateCode = false); + ~wxReader(); + + // Reads a component from XML. The return is the object ID, which can + // be used in calls to GetObject or GetObjectName. + // + // ISSUE: Still needs to implement references to objects. + // requires a bit of additional design in the XML (minor). + int ReadComponent(wxXmlNode *parent, wxIDepersist *Callbacks); + + // When streaming in, we may we depersisting to code, or to objects + // in memory. The depersist callbacks for generating code will + // not create the objects, but will create names for them instead. + // So internally, we keep track of IDs, not pointers. Depending + // on who you are in your callbacks, you can query the names or + // pointers of the objects as need be. You should not mix both, + // because you will die if you do. + + wxObject *GetObject(int id); + wxString GetObjectName(int id); + wxClassInfo *GetObjectClassInfo(int id) ; + + void SetObject(int id, wxObject *Object); + void SetObjectName(int id, const wxString &Name, wxClassInfo* ClassInfo); + + // Returns the result of a top level ReadComponent call. Used + // typically after you have instructed the reader to stream in an + // object, and you want the object back now. Only really valid if + // you are reading back in to an object in memory, as opposed to code. + wxObject *GetRoot() { return GetObject( 0 ) ; } +}; + +struct wxIDepersist +{ + // NotifyReader is called by wxReader so that we can have access to the + // object store functions in the reader when we are called back. Hmm. + // probably should have just made the callback functions each take a + // wxReader. + virtual void NotifyReader(wxReader *Reader) = 0; + + // The next three callbacks match the ACS model of creation of objects. + // At runtime, these will create actual instances, and manipulate them. + // When generating code, these will just create statements of C++ + // code to create the objects. + virtual void AllocateObject(int ObjectID, wxClassInfo *ClassInfo) = 0; + virtual void CreateObject(int ObjectID, + wxClassInfo *ClassInfo, + int ParamCount, + wxxVariant *VariantValues) = 0; + virtual void SetProperty(int ObjectID, + wxClassInfo *ClassInfo, + const wxPropertyInfo* PropertyInfo , + const wxxVariant &VariantValue) = 0; + virtual void SetConnect(int EventSourceObjectID, + wxClassInfo *EventSourceClassInfo, + int eventType , + const wxString &handlerName , + int EventSinkObjectID ) = 0; +}; + +/* + wxIDepersistRuntime implements the callbacks that will depersist + an object into a running memory image, as opposed to writing + C++ initialization code to bring the object to life. +*/ +class wxIDepersistRuntime : public wxIDepersist +{ + wxReader *Reader; +public: + virtual void NotifyReader(wxReader *_Reader) + { + Reader = _Reader; + } + virtual void AllocateObject(int ObjectID, wxClassInfo *ClassInfo); + virtual void CreateObject(int ObjectID, wxClassInfo *ClassInfo, int ParamCount, wxxVariant *VariantValues); + virtual void SetProperty(int ObjectID, wxClassInfo *ClassInfo, const wxPropertyInfo* PropertyInfo, const wxxVariant &VariantValue); + virtual void SetConnect(int EventSourceObjectID, + wxClassInfo *EventSourceClassInfo, + int eventType , + const wxString &handlerName , + int EventSinkObjectID ) ; +}; + +/* + wxIDepersistCode implements the callbacks that will depersist + an object into a C++ initialization function. +*/ + +class wxTextOutputStream ; + +class wxIDepersistCode : public wxIDepersist +{ + wxReader *Reader; + wxTextOutputStream *fp; +public: + wxIDepersistCode(wxTextOutputStream *out) : fp(out) { } + virtual void NotifyReader(wxReader *_Reader) + { + Reader = _Reader; + } + virtual void AllocateObject(int ObjectID, wxClassInfo *ClassInfo); + virtual void CreateObject(int ObjectID, wxClassInfo *ClassInfo, int ParamCount, wxxVariant *VariantValues); + virtual void SetProperty(int ObjectID, wxClassInfo *ClassInfo, const wxPropertyInfo* PropertyInfo, const wxxVariant &VariantValue); + virtual void SetConnect(int EventSourceObjectID, + wxClassInfo *EventSourceClassInfo, + int eventType , + const wxString &handlerName , + int EventSinkObjectID ) ; +}; + +#endif // wxUSE_EXTENDED_RTTI + +#endif \ No newline at end of file diff --git a/src/common/xtistrm.cpp b/src/common/xtistrm.cpp new file mode 100644 index 0000000000..146dca7b48 --- /dev/null +++ b/src/common/xtistrm.cpp @@ -0,0 +1,532 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/common/xtistrm.cpp +// Purpose: streaming runtime metadata information +// Author: Stefan Csomor +// Modified by: +// Created: 27/07/03 +// RCS-ID: $Id$ +// Copyright: (c) 2003 Stefan Csomor +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifdef __GNUG__ +#pragma implementation "xtistrm.h" +#endif + +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP + #include "wx/hash.h" + #include "wx/object.h" +#endif + +#include "wx/xml/xml.h" +#include "wx/tokenzr.h" +#include "wx/xtistrm.h" +#include "wx/txtstrm.h" + +#if wxUSE_EXTENDED_RTTI +#include +#include +#include + +using namespace std ; + +// ---------------------------------------------------------------------------- +// streaming xml out +// ---------------------------------------------------------------------------- + +void WriteComponent(wxObject *Object, const wxClassInfo *ClassInfo, wxXmlNode *parent, const wxString& nodeName , int &nextId , map< wxObject* , int > &writtenObjects ) ; + +void WriteComponentProperties( wxObject* obj , const wxClassInfo* ci , wxXmlNode *onode , int &nextId, map< wxObject* , int > &writtenObjects, map< string , int > &writtenProperties) +{ + const wxPropertyInfo *pi = ci->GetFirstProperty() ; + while( pi ) + { + if ( writtenProperties.find( pi->GetName() ) == writtenProperties.end() ) + { + writtenProperties[ pi->GetName() ] = 1 ; + const wxClassTypeInfo* cti = dynamic_cast< const wxClassTypeInfo* > ( pi->GetTypeInfo() ) ; + if ( cti ) + { + const wxClassInfo* pci = cti->GetClassInfo() ; + WriteComponent( pci->VariantToInstance( pi->GetAccessor()->GetProperty(obj) ) , pci , onode , pi->GetName() , nextId , writtenObjects ) ; + } + else + { + const wxDelegateTypeInfo* dti = dynamic_cast< const wxDelegateTypeInfo* > ( pi->GetTypeInfo() ) ; + if ( dti ) + { + // in which form should we stream out these ? + } + else + { + wxXmlNode *pnode; + pnode = new wxXmlNode(wxXML_ELEMENT_NODE, pi->GetName() ); + pi->GetAccessor()->WriteValue(pnode, obj ) ; + onode->AddChild(pnode); + } + } + } + pi = pi->GetNext() ; + } + const wxClassInfo** parents = ci->GetParents() ; + for ( int i = 0 ; parents[i] ; ++ i ) + { + WriteComponentProperties( obj , parents[i] , onode , nextId , writtenObjects , writtenProperties ) ; + } +} + +/* + Writing Components does have to take inheritance into account, that's why we are iterating + over our parents as well + */ + +void WriteComponent(wxObject *obj, const wxClassInfo *classInfo, wxXmlNode *parent, const wxString &nodeName) +{ + int nextid = 0 ; // 0 is the root element + map< wxObject* , int > writtenobjects ; + WriteComponent( obj , classInfo, parent, nodeName , nextid , writtenobjects ) ; +} + +void WriteComponent(wxObject *obj, const wxClassInfo *classInfo, wxXmlNode *parent, const wxString& nodeName , int &nextId, map< wxObject* , int > &writtenObjects ) +{ + map< string , int > writtenProperties ; + wxXmlNode *onode; + + onode = new wxXmlNode(wxXML_ELEMENT_NODE, nodeName); + + onode->AddProperty(wxString("class"), wxString(classInfo->GetClassName())); + if ( obj == NULL ) + { + wxXmlNode* nullnode = new wxXmlNode(wxXML_TEXT_NODE, wxEmptyString, "null"); + onode->AddChild(nullnode); + } + else + { + // if we have already written this object, just insert an id + if ( writtenObjects.find( obj ) != writtenObjects.end() ) + { + onode->AddProperty(wxString("id"), wxString::Format( "%d" , writtenObjects[obj] ) ); + } + else + { + int id = nextId++ ; + writtenObjects[obj] = id ; + onode->AddProperty(wxString("id"), wxString::Format( "%d" , id ) ); + WriteComponentProperties( obj , classInfo , onode , nextId , writtenObjects, writtenProperties) ; + } + } + + parent->AddChild(onode); +} + +// ---------------------------------------------------------------------------- +// reading xml in +// ---------------------------------------------------------------------------- + +wxxVariant wxReader::ReadPropertyValueNoAssign(wxXmlNode *Node, + wxClassInfo *ClassInfo, + const wxPropertyInfo * &pi , + wxIDepersist *Callbacks) +{ + wxxVariant res; + int ChildID; + + // form is: + // value + + //ISSUE: NULL component references are streamed out as "null" text + // node. This is not in keeping with the XML mindset. + + pi = ClassInfo->FindPropertyInfo(Node->GetName()); + if (!pi) + { + // error handling, please + assert(!"Property not found in extended class info"); + } + + const wxClassTypeInfo* cti = dynamic_cast< const wxClassTypeInfo* > ( pi->GetTypeInfo() ) ; + if ( cti ) + { + const wxClassInfo* eci = cti->GetClassInfo() ; + + ChildID = ReadComponent(Node , Callbacks); + if (ChildID != -1) + { + if (genCode) + res = wxxVariant(GetObjectName(ChildID)); + else + res = eci->InstanceToVariant(GetObject(ChildID)); + } + else + { + if (genCode) + res = wxxVariant(wxString("NULL")); + else + res = eci->InstanceToVariant(NULL); + } + } + else + { + const wxDelegateTypeInfo* dti = dynamic_cast< const wxDelegateTypeInfo* > ( pi->GetTypeInfo() ) ; + if ( dti ) + { + if (genCode) + { + // in which form should we code these ? + res = wxxVariant( wxXmlGetContentFromNode(Node) ) ; + } + else + { + res = wxxVariant( wxXmlGetContentFromNode(Node) ) ; + } + } + else + { + if (genCode) + { + if ( pi->GetTypeInfo()->GetKind() == wxT_STRING ) + res = wxxVariant( wxString::Format("wxString(\"%s\")",wxXmlGetContentFromNode(Node))); + else + res = wxxVariant( wxString::Format("%s(%s)",pi->GetTypeName(),wxXmlGetContentFromNode(Node) ) ); + } + else + res = pi->GetAccessor()->ReadValue(Node) ; + } + } + return res ; +} + +void wxReader::ReadPropertyValue(wxXmlNode *Node, + wxClassInfo *ClassInfo, + int ObjectID , + wxIDepersist *Callbacks) +{ + const wxPropertyInfo *pi; + wxxVariant res = ReadPropertyValueNoAssign( Node , ClassInfo, pi , Callbacks ) ; + + const wxDelegateTypeInfo* dti = dynamic_cast< const wxDelegateTypeInfo* > ( pi->GetTypeInfo() ) ; + + if ( dti ) + { + wxString resstring = res.Get() ; + wxInt32 pos = resstring.Find('.') ; + assert( pos != wxNOT_FOUND ) ; + int handlerOid = atol(resstring.Left(pos)) ; + wxString handlerName = resstring.Mid(pos+1) ; + + if (Callbacks) + Callbacks->SetConnect( ObjectID , ClassInfo , dti->GetEventType() , handlerName , handlerOid ) ; + } + else + { + if (Callbacks) + Callbacks->SetProperty(ObjectID, ClassInfo, pi , res); + } +} + +struct wxReader::wxReaderInternal +{ + /* + Code streamer will be storing names here. Runtime object streamer + will be storing actual pointers to objects here. The two are never + mixed. So the Objects array either has data, or the ObjectNames + array has data. Never both. Keyed by ObjectID (int) + */ + map Objects; + + map ObjectNames; + // only used when generating code, since the names loose the type info + map ObjectClasses; +}; + +wxReader::wxReader(bool GenerateCode) : genCode(GenerateCode) +{ + Data = new wxReaderInternal; +} + +wxReader::~wxReader() +{ + delete Data; +} + +wxObject *wxReader::GetObject(int id) +{ + assert( Data->Objects.find(id) != Data->Objects.end() ); + return Data->Objects[id]; +} + +wxString wxReader::GetObjectName(int id) +{ + assert( Data->ObjectNames.find(id) != Data->ObjectNames.end() ); + return wxString(Data->ObjectNames[id].c_str()); +} + +wxClassInfo* wxReader::GetObjectClassInfo(int id) +{ + assert( Data->ObjectClasses.find(id) != Data->ObjectClasses.end() ); + return Data->ObjectClasses[id] ; +} + +void wxReader::SetObject(int id, wxObject *Object) +{ + assert( Data->Objects.find(id) == Data->Objects.end() ) ; + Data->Objects[id] = Object; +} + +void wxReader::SetObjectName(int id, const wxString &Name, wxClassInfo *ClassInfo ) +{ + assert( Data->ObjectNames.find(id) == Data->ObjectNames.end() ) ; + Data->ObjectNames[id] = (const char *)Name; + Data->ObjectClasses[id] = ClassInfo ; +} + +/* + Reading components has not to be extended for components + as properties are always sought by typeinfo over all levels + and create params are always toplevel class only +*/ + +int wxReader::ReadComponent(wxXmlNode *Node, wxIDepersist *Callbacks) +{ + wxString ClassName; + wxClassInfo *ClassInfo; + + wxxVariant *CreateParams ; + wxXmlNode *Children; + int ObjectID; + + Callbacks->NotifyReader(this); + + Children = Node->GetChildren(); + if (!Node->GetPropVal("class", &ClassName)) + { + // No class name. Eek. FIXME: error handling + return -1; + } + ClassInfo = wxClassInfo::FindClass(ClassName); + if (Node->GetType() == wxXML_TEXT_NODE) + { + assert( wxXmlGetContentFromNode(Node) == "null" ) ; + // this must be a NULL component reference. We just bail out + return -1; + } + + wxString ObjectIdString ; + if (!Node->GetPropVal("id", &ObjectIdString)) + { + // No object id. Eek. FIXME: error handling + return -1; + } + + ObjectID = atoi( ObjectIdString.c_str() ) ; + // is this object already has been streamed in, return it here + if ( genCode ) + { + if ( Data->ObjectNames.find( ObjectID ) != Data->ObjectNames.end() ) + return ObjectID ; + } + else + { + if ( Data->Objects.find( ObjectID ) != Data->Objects.end() ) + return ObjectID ; + } + + // new object, start with allocation + Callbacks->AllocateObject(ObjectID, ClassInfo); + + // + // stream back the Create parameters first + CreateParams = new wxxVariant[ ClassInfo->GetCreateParamCount() ] ; + + typedef map PropertyNodes ; + typedef vector PropertyNames ; + + PropertyNodes propertyNodes ; + PropertyNames propertyNames ; + + while( Children ) + { + propertyNames.push_back( Children->GetName().c_str() ) ; + propertyNodes[Children->GetName().c_str()] = Children ; + Children = Children->GetNext() ; + } + + for ( int i = 0 ; i GetCreateParamCount() ; ++i ) + { + const wxChar* paramName = ClassInfo->GetCreateParamName(i) ; + PropertyNodes::iterator propiter = propertyNodes.find( paramName ) ; + // if we don't have the value of a create param set in the xml + // we use the default value + if ( propiter != propertyNodes.end() ) + { + wxXmlNode* prop = propiter->second ; + wxPropertyInfo* pi ; + CreateParams[i] = ReadPropertyValueNoAssign( prop , ClassInfo , pi , Callbacks ) ; + // CreateParams[i] = ClassInfo->FindPropertyInfo( ClassInfo->GetCreateParamName(i) ->GetAccessor()->ReadValue( prop ) ; + for ( size_t j = 0 ; j < propertyNames.size() ; ++j ) + { + if ( propertyNames[j] == paramName ) + { + propertyNames[j] = "" ; + break ; + } + } + } + else + { + CreateParams[i] = ClassInfo->FindPropertyInfo( paramName )->GetDefaultValue() ; + } + } + + // got the parameters. Call the Create method + Callbacks->CreateObject(ObjectID, + ClassInfo, + ClassInfo->GetCreateParamCount(), + &CreateParams[0]); + + // now stream in the rest of the properties, in the sequence their properties were written in the xml + for ( size_t j = 0 ; j < propertyNames.size() ; ++j ) + { + if ( propertyNames[j].length() ) + { + PropertyNodes::iterator propiter = propertyNodes.find( propertyNames[j] ) ; + if ( propiter != propertyNodes.end() ) + { + wxXmlNode* prop = propiter->second ; + string name = propiter->first ; + ReadPropertyValue( prop , ClassInfo , ObjectID , Callbacks ) ; + } + } + } + /* + for( PropertyNodes::iterator propiter = propertyNodes.begin() ; propiter != propertyNodes.end() ; propiter++ ) + { + wxXmlNode* prop = propiter->second ; + string name = propiter->first ; + ReadPropertyValue( prop , ClassInfo , ObjectID , Callbacks ) ; + } + */ + + // FIXME: if the list of children is not NULL now, then that means that + // there were properties in the XML not represented in the meta data + // this just needs error handling. + assert(!Children); + + delete[] CreateParams ; + + return ObjectID; +} + +// ---------------------------------------------------------------------------- +// depersisting to memory +// ---------------------------------------------------------------------------- + +void wxIDepersistRuntime::AllocateObject(int ObjectID, wxClassInfo *ClassInfo) +{ + wxObject *O; + O = ClassInfo->CreateObject(); + Reader->SetObject(ObjectID, O); +} + +void wxIDepersistRuntime::CreateObject(int ObjectID, + wxClassInfo *ClassInfo, + int ParamCount, + wxxVariant *Params) +{ + wxObject *O; + O = Reader->GetObject(ObjectID); + ClassInfo->Create(O, ParamCount, Params); +} + +void wxIDepersistRuntime::SetProperty(int ObjectID, + wxClassInfo *WXUNUSED(ClassInfo), + const wxPropertyInfo* PropertyInfo, + const wxxVariant &Value) +{ + wxObject *O; + O = Reader->GetObject(ObjectID); + PropertyInfo->GetAccessor()->SetProperty( O , Value ) ; +} + +void wxIDepersistRuntime::SetConnect(int EventSourceObjectID, + wxClassInfo *WXUNUSED(EventSourceClassInfo), + int eventType , + const wxString &handlerName , + int EventSinkObjectID ) +{ + wxWindow *ehsource = dynamic_cast< wxWindow* >( Reader->GetObject( EventSourceObjectID ) ) ; + wxEvtHandler *ehsink = dynamic_cast< wxEvtHandler *>(Reader->GetObject(EventSinkObjectID) ) ; + + if ( ehsource && ehsink ) + { + ehsource->Connect( ehsource->GetId() , eventType , + ehsink->GetClassInfo()->FindHandlerInfo(handlerName)->GetEventFunction() , NULL /*user data*/ , + ehsink ) ; + } +} + +// ---------------------------------------------------------------------------- +// depersisting to code +// ---------------------------------------------------------------------------- + + +void wxIDepersistCode::AllocateObject(int ObjectID, wxClassInfo *ClassInfo) +{ + wxString objectName = wxString::Format( "LocalObject_%d" , ObjectID ) ; + fp->WriteString( wxString::Format( "\t%s *%s = new %s;\n", + ClassInfo->GetClassName(), + objectName, + ClassInfo->GetClassName()) ); + Reader->SetObjectName(ObjectID, objectName, ClassInfo); +} + +void wxIDepersistCode::CreateObject(int ObjectID, + wxClassInfo *WXUNUSED(ClassInfo), + int ParamCount, + wxxVariant *Params) +{ + int i; + fp->WriteString( wxString::Format( "\t%s->Create(", Reader->GetObjectName(ObjectID) ) ); + for (i = 0; i < ParamCount; i++) + { + fp->WriteString( wxString::Format( "%s", (const char *)Params[i].Get() ) ); + if (i < ParamCount - 1) + fp->WriteString( ", "); + } + fp->WriteString( ");\n"); +} + +void wxIDepersistCode::SetProperty(int ObjectID, + wxClassInfo *WXUNUSED(ClassInfo), + const wxPropertyInfo* PropertyInfo, + const wxxVariant &Value) +{ + wxString d = Value.Get() ; + fp->WriteString( wxString::Format( "\t%s->%s(%s);\n", + Reader->GetObjectName(ObjectID), + PropertyInfo->GetAccessor()->GetSetterName(), + d) ); +} + +void wxIDepersistCode::SetConnect(int EventSourceObjectID, + wxClassInfo *WXUNUSED(EventSourceClassInfo), + int eventType , + const wxString &handlerName , + int EventSinkObjectID ) +{ + wxString ehsource = Reader->GetObjectName( EventSourceObjectID ) ; + wxString ehsink = Reader->GetObjectName(EventSinkObjectID) ; + wxString ehsinkClass = Reader->GetObjectClassInfo(EventSinkObjectID)->GetClassName() ; + + fp->WriteString( wxString::Format( "\t%s->Connect( %s->GetId() , %d , (wxObjectEventFunction)(wxEventFunction) & %s::%s , NULL , %s ) ;" , + ehsource , ehsource , eventType , ehsinkClass , handlerName , ehsink ) ); +} + +#endif