1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/xtistrm.cpp 
   3 // Purpose:     streaming runtime metadata information 
   4 // Author:      Stefan Csomor 
   8 // Copyright:   (c) 2003 Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  13 #pragma implementation "xtistrm.h" 
  16 // For compilers that support precompilation, includes "wx.h". 
  17 #include "wx/wxprec.h" 
  25 #include "wx/object.h" 
  28 #include "wx/xml/xml.h" 
  29 #include "wx/tokenzr.h" 
  30 #include "wx/txtstrm.h" 
  33 #if wxUSE_EXTENDED_RTTI 
  35 #include "wx/xtistrm.h" 
  36 #include "wx/xtixml.h" 
  38 #include "wx/beforestd.h" 
  42 #include "wx/afterstd.h" 
  50 // convenience functions 
  52 void wxXmlAddContentToNode( wxXmlNode
* node 
, const wxString
& data 
) 
  54     node
->AddChild(new wxXmlNode(wxXML_TEXT_NODE
, wxT("value"), data 
) ); 
  57 wxString 
wxXmlGetContentFromNode( wxXmlNode 
*node 
) 
  59     if ( node
->GetChildren() ) 
  60         return node
->GetChildren()->GetContent() ; 
  62         return wxEmptyString 
; 
  65 struct wxXmlWriter::wxXmlWriterInternal
 
  68     wxXmlNode 
*m_current 
; 
  69     vector
< wxXmlNode 
* > m_objectStack 
; 
  71     void Push( wxXmlNode 
*newCurrent 
) 
  73         m_objectStack
.push_back( m_current 
) ; 
  74         m_current 
= newCurrent 
; 
  79         m_current 
= m_objectStack
.back() ; 
  80         m_objectStack
.pop_back() ; 
  84 wxXmlWriter::wxXmlWriter( wxXmlNode 
* rootnode 
) 
  86     m_data 
= new wxXmlWriterInternal() ; 
  87     m_data
->m_root 
= rootnode 
; 
  88     m_data
->m_current 
= rootnode 
; 
  91 wxXmlWriter::~wxXmlWriter() 
  96 void wxXmlWriter::DoBeginWriteTopLevelEntry( const wxString 
&name 
) 
  99     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("entry")); 
 100     pnode
->AddProperty(wxString(wxT("name")), name
); 
 101     m_data
->m_current
->AddChild(pnode
) ; 
 102     m_data
->Push( pnode 
) ; 
 105 void wxXmlWriter::DoEndWriteTopLevelEntry( const wxString 
&WXUNUSED(name
) ) 
 110 void wxXmlWriter::DoBeginWriteObject(const wxObject 
*WXUNUSED(object
), const wxClassInfo 
*classInfo
, int objectID 
, wxxVariantArray 
&metadata   
) 
 113     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object")); 
 114     pnode
->AddProperty(wxT("class"), wxString(classInfo
->GetClassName())); 
 115     pnode
->AddProperty(wxT("id"), wxString::Format( wxT("%d") , objectID 
) ); 
 117     for ( size_t i 
= 0 ; i 
< metadata
.GetCount() ; ++i 
) 
 119         pnode
->AddProperty( metadata
[i
].GetName() , metadata
[i
].GetAsString() ) ; 
 121     m_data
->m_current
->AddChild(pnode
) ; 
 122     m_data
->Push( pnode 
) ; 
 125 // end of writing the root object 
 126 void wxXmlWriter::DoEndWriteObject(const wxObject 
*WXUNUSED(object
), const wxClassInfo 
*WXUNUSED(classInfo
), int WXUNUSED(objectID
) ) 
 131 // writes a property in the stream format 
 132 void wxXmlWriter::DoWriteSimpleType( wxxVariant 
&value 
) 
 134     wxXmlAddContentToNode( m_data
->m_current 
,value
.GetAsString() ) ; 
 137 void wxXmlWriter::DoBeginWriteElement() 
 140     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("element") ); 
 141     m_data
->m_current
->AddChild(pnode
) ; 
 142     m_data
->Push( pnode 
) ; 
 145 void wxXmlWriter::DoEndWriteElement() 
 150 void wxXmlWriter::DoBeginWriteProperty(const wxPropertyInfo 
*pi 
) 
 153     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("prop") ); 
 154     pnode
->AddProperty(wxT("name"), pi
->GetName() ); 
 155     m_data
->m_current
->AddChild(pnode
) ; 
 156     m_data
->Push( pnode 
) ; 
 159 void wxXmlWriter::DoEndWriteProperty(const wxPropertyInfo 
*WXUNUSED(propInfo
) ) 
 166 // insert an object reference to an already written object 
 167 void wxXmlWriter::DoWriteRepeatedObject( int objectID 
) 
 170     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object")); 
 171     pnode
->AddProperty(wxString(wxT("href")), wxString::Format( wxT("%d") , objectID 
) ); 
 172     m_data
->m_current
->AddChild(pnode
) ; 
 175 // insert a null reference 
 176 void wxXmlWriter::DoWriteNullObject() 
 179     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object")); 
 180     m_data
->m_current
->AddChild(pnode
) ; 
 183 // writes a delegate in the stream format 
 184 void wxXmlWriter::DoWriteDelegate( const wxObject 
*WXUNUSED(object
),  const wxClassInfo
* WXUNUSED(classInfo
) , const wxPropertyInfo 
*WXUNUSED(pi
) , 
 185                                   const wxObject 
*eventSink
, int sinkObjectID 
, const wxClassInfo
* WXUNUSED(eventSinkClassInfo
) , const wxHandlerInfo
* handlerInfo 
) 
 187     if ( eventSink 
!= NULL 
&& handlerInfo 
!= NULL 
) 
 189         wxXmlAddContentToNode( m_data
->m_current 
,wxString::Format(wxT("%d.%s"), sinkObjectID 
, handlerInfo
->GetName().c_str()) ) ; 
 193 // ---------------------------------------------------------------------------- 
 194 // reading objects in 
 195 // ---------------------------------------------------------------------------- 
 199 // ---------------------------------------------------------------------------- 
 201 // ---------------------------------------------------------------------------- 
 204 Reading components has not to be extended for components 
 205 as properties are always sought by typeinfo over all levels 
 206 and create params are always toplevel class only 
 209 int wxXmlReader::ReadComponent(wxXmlNode 
*node
, wxDepersister 
*callbacks
) 
 211     wxASSERT_MSG( callbacks 
, wxT("Does not support reading without a Depersistor") ) ; 
 213     wxClassInfo 
*classInfo
; 
 215     wxxVariant 
*createParams 
; 
 216     int *createParamOids 
; 
 217     const wxClassInfo
** createClassInfos 
; 
 220     wxString ObjectIdString 
; 
 222     children 
= node
->GetChildren(); 
 225         // check for a null object or href 
 226         if (node
->GetPropVal(wxT("href") , &ObjectIdString 
) ) 
 228             objectID 
= atoi( ObjectIdString
.ToAscii() ) ; 
 229             if ( HasObjectClassInfo( objectID 
) ) 
 235                 wxLogError( _("Forward hrefs are not supported") ) ; 
 236                 return wxInvalidObjectID 
;                 
 239         if ( !node
->GetPropVal(wxT("id") , &ObjectIdString 
) ) 
 241             return wxNullObjectID
; 
 244     if (!node
->GetPropVal(wxT("class"), &className
)) 
 246         // No class name.  Eek. FIXME: error handling 
 247         return wxInvalidObjectID
; 
 249     classInfo 
= wxClassInfo::FindClass(className
); 
 250     if ( classInfo 
== NULL 
) 
 252         wxLogError( wxString::Format(_("unknown class %s"),className 
) ) ; 
 253         return wxInvalidObjectID 
; 
 256     if ( children 
!= NULL 
&& children
->GetType() == wxXML_TEXT_NODE 
) 
 258         wxLogError(_("objects cannot have XML Text Nodes") ) ;  
 259         return wxInvalidObjectID
; 
 261     if (!node
->GetPropVal(wxT("id"), &ObjectIdString
)) 
 263         wxLogError(_("Objects must have an id attribute") ) ; 
 264         // No object id.  Eek. FIXME: error handling 
 265         return wxInvalidObjectID
; 
 267     objectID 
= atoi( ObjectIdString
.ToAscii() ) ; 
 268     // is this object already has been streamed in, return it here 
 269     if ( HasObjectClassInfo( objectID 
) ) 
 271         wxLogError ( wxString::Format(_("Doubly used id : %d"), objectID 
) ) ;  
 272         return wxInvalidObjectID 
; 
 275     // new object, start with allocation 
 276     // first make the object know to our internal registry 
 277     SetObjectClassInfo( objectID 
, classInfo 
) ; 
 279     wxxVariantArray metadata 
; 
 280     wxXmlProperty 
*xp 
= node
->GetProperties() ; 
 283         if ( xp
->GetName() != wxString(wxT("class")) && xp
->GetName() != wxString(wxT("id")) ) 
 285             metadata
.Add( new wxxVariant( xp
->GetValue() , xp
->GetName() ) ) ; 
 289     if ( !classInfo
->NeedsDirectConstruction() ) 
 290         callbacks
->AllocateObject(objectID
, classInfo
, metadata
); 
 293     // stream back the Create parameters first 
 294     createParams 
= new wxxVariant
[ classInfo
->GetCreateParamCount() ] ; 
 295     createParamOids 
= new int[classInfo
->GetCreateParamCount() ] ; 
 296     createClassInfos 
= new const wxClassInfo
*[classInfo
->GetCreateParamCount() ] ; 
 299     typedef map
<wstring
, wxXmlNode 
*> PropertyNodes 
; 
 300     typedef vector
<wstring
> PropertyNames 
; 
 302     typedef map
<string
, wxXmlNode 
*> PropertyNodes 
; 
 303     typedef vector
<string
> PropertyNames 
; 
 305     PropertyNodes propertyNodes 
; 
 306     PropertyNames propertyNames 
; 
 311         children
->GetPropVal( wxT("name") , &name 
) ; 
 312         propertyNames
.push_back( name
.c_str() ) ; 
 313         propertyNodes
[name
.c_str()] = children
->GetChildren() ; 
 314         children 
= children
->GetNext() ; 
 317     for ( int i 
= 0 ; i 
<classInfo
->GetCreateParamCount() ; ++i 
) 
 319         const wxChar
* paramName 
= classInfo
->GetCreateParamName(i
) ; 
 320         PropertyNodes::iterator propiter 
= propertyNodes
.find( paramName 
) ; 
 321         const wxPropertyInfo
* pi 
= classInfo
->FindPropertyInfo( paramName 
) ; 
 324             wxLogError( wxString::Format(_("Unkown Property %s"),paramName
) ) ; 
 326         // if we don't have the value of a create param set in the xml 
 327         // we use the default value 
 328         if ( propiter 
!= propertyNodes
.end() ) 
 330             wxXmlNode
* prop 
= propiter
->second 
; 
 331             if ( pi
->GetTypeInfo()->IsObjectType() ) 
 333                 createParamOids
[i
] = ReadComponent( prop 
, callbacks 
) ; 
 334                 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ; 
 338                 createParamOids
[i
] = wxInvalidObjectID 
; 
 339                 createParams
[i
] = ReadValue( prop 
, pi
->GetTypeInfo() ) ; 
 340                 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG 
) 
 342                     const wxEnumTypeInfo 
*eti 
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ; 
 346                         eti
->ConvertToLong( createParams
[i
]  , realval 
) ; 
 347                         createParams
[i
] = wxxVariant( realval 
) ; 
 351                         wxLogError( _("Type must have enum - long conversion") ) ; 
 354                 createClassInfos
[i
] = NULL 
; 
 357             for ( size_t j 
= 0 ; j 
< propertyNames
.size() ; ++j 
) 
 359                 if ( propertyNames
[j
] == paramName 
) 
 361                     propertyNames
[j
] = wxEmptyString 
; 
 368             if ( pi
->GetTypeInfo()->IsObjectType() ) 
 370                 createParamOids
[i
] = wxNullObjectID 
; 
 371                 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ; 
 375                 createParams
[i
] = pi
->GetDefaultValue() ; 
 376                 createParamOids
[i
] = wxInvalidObjectID 
; 
 381     // got the parameters.  Call the Create method 
 382     if ( classInfo
->NeedsDirectConstruction() ) 
 383         callbacks
->ConstructObject(objectID
, classInfo
, 
 384             classInfo
->GetCreateParamCount(), 
 385             createParams
, createParamOids
, createClassInfos
, metadata 
); 
 387         callbacks
->CreateObject(objectID
, classInfo
, 
 388             classInfo
->GetCreateParamCount(), 
 389             createParams
, createParamOids
, createClassInfos
, metadata 
); 
 391     // now stream in the rest of the properties, in the sequence their properties were written in the xml 
 392     for ( size_t j 
= 0 ; j 
< propertyNames
.size() ; ++j 
) 
 394         if ( propertyNames
[j
].length() ) 
 396             PropertyNodes::iterator propiter 
= propertyNodes
.find( propertyNames
[j
] ) ; 
 397             if ( propiter 
!= propertyNodes
.end() ) 
 399                 wxXmlNode
* prop 
= propiter
->second 
; 
 400                 const wxPropertyInfo
* pi 
= classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() ) ; 
 401                 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION 
) 
 403                     const wxCollectionTypeInfo
* collType 
= dynamic_cast< const wxCollectionTypeInfo
* >( pi
->GetTypeInfo() ) ; 
 404                     const wxTypeInfo 
* elementType 
= collType
->GetElementType() ; 
 407                         if ( prop
->GetName() != wxT("element") ) 
 409                             wxLogError( _("A non empty collection must consist of 'element' nodes" ) ) ; 
 412                         wxXmlNode
* elementContent 
= prop
->GetChildren() ; 
 413                         if ( elementContent 
) 
 415                             // we skip empty elements 
 416                             if ( elementType
->IsObjectType() ) 
 418                                 int valueId 
= ReadComponent( elementContent 
, callbacks 
) ; 
 419                                 if ( valueId 
!= wxInvalidObjectID 
) 
 421                                     if ( pi
->GetAccessor()->HasAdder() ) 
 422                                         callbacks
->AddToPropertyCollectionAsObject( objectID 
, classInfo 
, pi 
, valueId 
) ; 
 423                                     // TODO for collections we must have a notation on taking over ownership or not 
 424                                     if ( elementType
->GetKind() == wxT_OBJECT 
&& valueId 
!= wxNullObjectID 
) 
 425                                         callbacks
->DestroyObject( valueId 
, GetObjectClassInfo( valueId 
) ) ; 
 430                                 wxxVariant elementValue 
= ReadValue( elementContent 
, elementType 
) ; 
 431                                 if ( pi
->GetAccessor()->HasAdder() ) 
 432                                     callbacks
->AddToPropertyCollection( objectID 
, classInfo 
,pi 
, elementValue 
) ; 
 435                         prop 
= prop
->GetNext() ; 
 438                 else if ( pi
->GetTypeInfo()->IsObjectType() ) 
 440                     // and object can either be streamed out a string or as an object 
 441                     // in case we have no node, then the object's streaming out has been vetoed 
 444                         if ( prop
->GetName() == wxT("object") ) 
 446                             int valueId 
= ReadComponent( prop 
, callbacks 
) ; 
 447                             if ( valueId 
!= wxInvalidObjectID 
) 
 449                                 callbacks
->SetPropertyAsObject( objectID 
, classInfo 
, pi 
, valueId 
) ; 
 450                                 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT 
&& valueId 
!= wxNullObjectID 
) 
 451                                     callbacks
->DestroyObject( valueId 
, GetObjectClassInfo( valueId 
) ) ; 
 456                             wxASSERT( pi
->GetTypeInfo()->HasStringConverters() ) ; 
 457                             wxxVariant nodeval 
= ReadValue( prop 
, pi
->GetTypeInfo() ) ; 
 458                             callbacks
->SetProperty( objectID
, classInfo 
,pi 
, nodeval 
) ; 
 462                 else if ( pi
->GetTypeInfo()->IsDelegateType() ) 
 466                         wxString resstring 
= prop
->GetContent() ; 
 467                         wxInt32 pos 
= resstring
.Find('.') ; 
 468                         if ( pos 
!= wxNOT_FOUND 
) 
 470                             int sinkOid 
= atol(resstring
.Left(pos
).ToAscii()) ; 
 471                             wxString handlerName 
= resstring
.Mid(pos
+1) ; 
 472                             wxClassInfo
* sinkClassInfo 
= GetObjectClassInfo( sinkOid 
) ; 
 474                             callbacks
->SetConnect( objectID 
, classInfo 
, pi 
, sinkClassInfo 
, 
 475                                 sinkClassInfo
->FindHandlerInfo(handlerName
) ,  sinkOid 
) ; 
 479                             wxLogError( _("incorrect event handler string, missing dot") ) ; 
 486                     wxxVariant nodeval 
= ReadValue( prop 
, pi
->GetTypeInfo() ) ; 
 487                     if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG 
) 
 489                         const wxEnumTypeInfo 
*eti 
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ; 
 493                             eti
->ConvertToLong( nodeval 
, realval 
) ; 
 494                             nodeval 
= wxxVariant( realval 
) ; 
 498                             wxLogError( _("Type must have enum - long conversion") ) ; 
 501                     callbacks
->SetProperty( objectID
, classInfo 
,pi 
, nodeval 
) ; 
 507     delete[] createParams 
; 
 508     delete[] createParamOids 
; 
 509     delete[] createClassInfos 
; 
 514 wxxVariant 
wxXmlReader::ReadValue(wxXmlNode 
*node
, 
 515                                   const wxTypeInfo 
*type 
) 
 519         content 
= node
->GetContent() ; 
 521     type
->ConvertFromString( content 
, result 
) ; 
 525 int wxXmlReader::ReadObject( const wxString 
&name 
, wxDepersister 
*callbacks
) 
 527     wxXmlNode 
*iter 
= m_parent
->GetChildren() ; 
 531         if ( iter
->GetPropVal(wxT("name"), &entryName
) ) 
 533             if ( entryName 
== name 
) 
 534                 return ReadComponent( iter
->GetChildren() , callbacks 
) ; 
 536         iter 
= iter
->GetNext() ; 
 538     return wxInvalidObjectID 
;