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 // For compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  21 #include "wx/object.h" 
  24 #include "wx/xml/xml.h" 
  25 #include "wx/tokenzr.h" 
  26 #include "wx/txtstrm.h" 
  29 #if wxUSE_EXTENDED_RTTI 
  31 #include "wx/xtistrm.h" 
  32 #include "wx/xtixml.h" 
  34 #include "wx/beforestd.h" 
  38 #include "wx/afterstd.h" 
  46 // convenience functions 
  48 void wxXmlAddContentToNode( wxXmlNode
* node 
, const wxString
& data 
) 
  50     node
->AddChild(new wxXmlNode(wxXML_TEXT_NODE
, wxT("value"), data 
) ); 
  53 wxString 
wxXmlGetContentFromNode( wxXmlNode 
*node 
) 
  55     if ( node
->GetChildren() ) 
  56         return node
->GetChildren()->GetContent() ; 
  58         return wxEmptyString 
; 
  61 struct wxXmlWriter::wxXmlWriterInternal
 
  64     wxXmlNode 
*m_current 
; 
  65     vector
< wxXmlNode 
* > m_objectStack 
; 
  67     void Push( wxXmlNode 
*newCurrent 
) 
  69         m_objectStack
.push_back( m_current 
) ; 
  70         m_current 
= newCurrent 
; 
  75         m_current 
= m_objectStack
.back() ; 
  76         m_objectStack
.pop_back() ; 
  80 wxXmlWriter::wxXmlWriter( wxXmlNode 
* rootnode 
) 
  82     m_data 
= new wxXmlWriterInternal() ; 
  83     m_data
->m_root 
= rootnode 
; 
  84     m_data
->m_current 
= rootnode 
; 
  87 wxXmlWriter::~wxXmlWriter() 
  92 void wxXmlWriter::DoBeginWriteTopLevelEntry( const wxString 
&name 
) 
  95     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("entry")); 
  96     pnode
->AddProperty(wxString(wxT("name")), name
); 
  97     m_data
->m_current
->AddChild(pnode
) ; 
  98     m_data
->Push( pnode 
) ; 
 101 void wxXmlWriter::DoEndWriteTopLevelEntry( const wxString 
&WXUNUSED(name
) ) 
 106 void wxXmlWriter::DoBeginWriteObject(const wxObject 
*WXUNUSED(object
), const wxClassInfo 
*classInfo
, int objectID 
, wxxVariantArray 
&metadata   
) 
 109     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object")); 
 110     pnode
->AddProperty(wxT("class"), wxString(classInfo
->GetClassName())); 
 111     pnode
->AddProperty(wxT("id"), wxString::Format( wxT("%d") , objectID 
) ); 
 113     for ( size_t i 
= 0 ; i 
< metadata
.GetCount() ; ++i 
) 
 115         pnode
->AddProperty( metadata
[i
].GetName() , metadata
[i
].GetAsString() ) ; 
 117     m_data
->m_current
->AddChild(pnode
) ; 
 118     m_data
->Push( pnode 
) ; 
 121 // end of writing the root object 
 122 void wxXmlWriter::DoEndWriteObject(const wxObject 
*WXUNUSED(object
), const wxClassInfo 
*WXUNUSED(classInfo
), int WXUNUSED(objectID
) ) 
 127 // writes a property in the stream format 
 128 void wxXmlWriter::DoWriteSimpleType( wxxVariant 
&value 
) 
 130     wxXmlAddContentToNode( m_data
->m_current 
,value
.GetAsString() ) ; 
 133 void wxXmlWriter::DoBeginWriteElement() 
 136     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("element") ); 
 137     m_data
->m_current
->AddChild(pnode
) ; 
 138     m_data
->Push( pnode 
) ; 
 141 void wxXmlWriter::DoEndWriteElement() 
 146 void wxXmlWriter::DoBeginWriteProperty(const wxPropertyInfo 
*pi 
) 
 149     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("prop") ); 
 150     pnode
->AddProperty(wxT("name"), pi
->GetName() ); 
 151     m_data
->m_current
->AddChild(pnode
) ; 
 152     m_data
->Push( pnode 
) ; 
 155 void wxXmlWriter::DoEndWriteProperty(const wxPropertyInfo 
*WXUNUSED(propInfo
) ) 
 162 // insert an object reference to an already written object 
 163 void wxXmlWriter::DoWriteRepeatedObject( int objectID 
) 
 166     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object")); 
 167     pnode
->AddProperty(wxString(wxT("href")), wxString::Format( wxT("%d") , objectID 
) ); 
 168     m_data
->m_current
->AddChild(pnode
) ; 
 171 // insert a null reference 
 172 void wxXmlWriter::DoWriteNullObject() 
 175     pnode 
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object")); 
 176     m_data
->m_current
->AddChild(pnode
) ; 
 179 // writes a delegate in the stream format 
 180 void wxXmlWriter::DoWriteDelegate( const wxObject 
*WXUNUSED(object
),  const wxClassInfo
* WXUNUSED(classInfo
) , const wxPropertyInfo 
*WXUNUSED(pi
) , 
 181                                   const wxObject 
*eventSink
, int sinkObjectID 
, const wxClassInfo
* WXUNUSED(eventSinkClassInfo
) , const wxHandlerInfo
* handlerInfo 
) 
 183     if ( eventSink 
!= NULL 
&& handlerInfo 
!= NULL 
) 
 185         wxXmlAddContentToNode( m_data
->m_current 
,wxString::Format(wxT("%d.%s"), sinkObjectID 
, handlerInfo
->GetName().c_str()) ) ; 
 189 // ---------------------------------------------------------------------------- 
 190 // reading objects in 
 191 // ---------------------------------------------------------------------------- 
 195 // ---------------------------------------------------------------------------- 
 197 // ---------------------------------------------------------------------------- 
 200 Reading components has not to be extended for components 
 201 as properties are always sought by typeinfo over all levels 
 202 and create params are always toplevel class only 
 205 int wxXmlReader::ReadComponent(wxXmlNode 
*node
, wxDepersister 
*callbacks
) 
 207     wxASSERT_MSG( callbacks 
, wxT("Does not support reading without a Depersistor") ) ; 
 209     wxClassInfo 
*classInfo
; 
 211     wxxVariant 
*createParams 
; 
 212     int *createParamOids 
; 
 213     const wxClassInfo
** createClassInfos 
; 
 216     wxString ObjectIdString 
; 
 218     children 
= node
->GetChildren(); 
 221         // check for a null object or href 
 222         if (node
->GetPropVal(wxT("href") , &ObjectIdString 
) ) 
 224             objectID 
= atoi( ObjectIdString
.ToAscii() ) ; 
 225             if ( HasObjectClassInfo( objectID 
) ) 
 231                 wxLogError( _("Forward hrefs are not supported") ) ; 
 232                 return wxInvalidObjectID 
; 
 235         if ( !node
->GetPropVal(wxT("id") , &ObjectIdString 
) ) 
 237             return wxNullObjectID
; 
 240     if (!node
->GetPropVal(wxT("class"), &className
)) 
 242         // No class name.  Eek. FIXME: error handling 
 243         return wxInvalidObjectID
; 
 245     classInfo 
= wxClassInfo::FindClass(className
); 
 246     if ( classInfo 
== NULL 
) 
 248         wxLogError( wxString::Format(_("unknown class %s"),className 
) ) ; 
 249         return wxInvalidObjectID 
; 
 252     if ( children 
!= NULL 
&& children
->GetType() == wxXML_TEXT_NODE 
) 
 254         wxLogError(_("objects cannot have XML Text Nodes") ) ; 
 255         return wxInvalidObjectID
; 
 257     if (!node
->GetPropVal(wxT("id"), &ObjectIdString
)) 
 259         wxLogError(_("Objects must have an id attribute") ) ; 
 260         // No object id.  Eek. FIXME: error handling 
 261         return wxInvalidObjectID
; 
 263     objectID 
= atoi( ObjectIdString
.ToAscii() ) ; 
 264     // is this object already has been streamed in, return it here 
 265     if ( HasObjectClassInfo( objectID 
) ) 
 267         wxLogError ( wxString::Format(_("Doubly used id : %d"), objectID 
) ) ; 
 268         return wxInvalidObjectID 
; 
 271     // new object, start with allocation 
 272     // first make the object know to our internal registry 
 273     SetObjectClassInfo( objectID 
, classInfo 
) ; 
 275     wxxVariantArray metadata 
; 
 276     wxXmlProperty 
*xp 
= node
->GetProperties() ; 
 279         if ( xp
->GetName() != wxString(wxT("class")) && xp
->GetName() != wxString(wxT("id")) ) 
 281             metadata
.Add( new wxxVariant( xp
->GetValue() , xp
->GetName() ) ) ; 
 285     if ( !classInfo
->NeedsDirectConstruction() ) 
 286         callbacks
->AllocateObject(objectID
, classInfo
, metadata
); 
 289     // stream back the Create parameters first 
 290     createParams 
= new wxxVariant
[ classInfo
->GetCreateParamCount() ] ; 
 291     createParamOids 
= new int[classInfo
->GetCreateParamCount() ] ; 
 292     createClassInfos 
= new const wxClassInfo
*[classInfo
->GetCreateParamCount() ] ; 
 295     typedef map
<wstring
, wxXmlNode 
*> PropertyNodes 
; 
 296     typedef vector
<wstring
> PropertyNames 
; 
 298     typedef map
<string
, wxXmlNode 
*> PropertyNodes 
; 
 299     typedef vector
<string
> PropertyNames 
; 
 301     PropertyNodes propertyNodes 
; 
 302     PropertyNames propertyNames 
; 
 307         children
->GetPropVal( wxT("name") , &name 
) ; 
 308         propertyNames
.push_back( name
.c_str() ) ; 
 309         propertyNodes
[name
.c_str()] = children
->GetChildren() ; 
 310         children 
= children
->GetNext() ; 
 313     for ( int i 
= 0 ; i 
<classInfo
->GetCreateParamCount() ; ++i 
) 
 315         const wxChar
* paramName 
= classInfo
->GetCreateParamName(i
) ; 
 316         PropertyNodes::iterator propiter 
= propertyNodes
.find( paramName 
) ; 
 317         const wxPropertyInfo
* pi 
= classInfo
->FindPropertyInfo( paramName 
) ; 
 320             wxLogError( wxString::Format(_("Unkown Property %s"),paramName
) ) ; 
 322         // if we don't have the value of a create param set in the xml 
 323         // we use the default value 
 324         if ( propiter 
!= propertyNodes
.end() ) 
 326             wxXmlNode
* prop 
= propiter
->second 
; 
 327             if ( pi
->GetTypeInfo()->IsObjectType() ) 
 329                 createParamOids
[i
] = ReadComponent( prop 
, callbacks 
) ; 
 330                 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ; 
 334                 createParamOids
[i
] = wxInvalidObjectID 
; 
 335                 createParams
[i
] = ReadValue( prop 
, pi
->GetTypeInfo() ) ; 
 336                 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG 
) 
 338                     const wxEnumTypeInfo 
*eti 
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ; 
 342                         eti
->ConvertToLong( createParams
[i
]  , realval 
) ; 
 343                         createParams
[i
] = wxxVariant( realval 
) ; 
 347                         wxLogError( _("Type must have enum - long conversion") ) ; 
 350                 createClassInfos
[i
] = NULL 
; 
 353             for ( size_t j 
= 0 ; j 
< propertyNames
.size() ; ++j 
) 
 355                 if ( propertyNames
[j
] == paramName 
) 
 357                     propertyNames
[j
] = wxEmptyString 
; 
 364             if ( pi
->GetTypeInfo()->IsObjectType() ) 
 366                 createParamOids
[i
] = wxNullObjectID 
; 
 367                 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ; 
 371                 createParams
[i
] = pi
->GetDefaultValue() ; 
 372                 createParamOids
[i
] = wxInvalidObjectID 
; 
 377     // got the parameters.  Call the Create method 
 378     if ( classInfo
->NeedsDirectConstruction() ) 
 379         callbacks
->ConstructObject(objectID
, classInfo
, 
 380             classInfo
->GetCreateParamCount(), 
 381             createParams
, createParamOids
, createClassInfos
, metadata 
); 
 383         callbacks
->CreateObject(objectID
, classInfo
, 
 384             classInfo
->GetCreateParamCount(), 
 385             createParams
, createParamOids
, createClassInfos
, metadata 
); 
 387     // now stream in the rest of the properties, in the sequence their properties were written in the xml 
 388     for ( size_t j 
= 0 ; j 
< propertyNames
.size() ; ++j 
) 
 390         if ( propertyNames
[j
].length() ) 
 392             PropertyNodes::iterator propiter 
= propertyNodes
.find( propertyNames
[j
] ) ; 
 393             if ( propiter 
!= propertyNodes
.end() ) 
 395                 wxXmlNode
* prop 
= propiter
->second 
; 
 396                 const wxPropertyInfo
* pi 
= classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() ) ; 
 397                 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION 
) 
 399                     const wxCollectionTypeInfo
* collType 
= dynamic_cast< const wxCollectionTypeInfo
* >( pi
->GetTypeInfo() ) ; 
 400                     const wxTypeInfo 
* elementType 
= collType
->GetElementType() ; 
 403                         if ( prop
->GetName() != wxT("element") ) 
 405                             wxLogError( _("A non empty collection must consist of 'element' nodes" ) ) ; 
 408                         wxXmlNode
* elementContent 
= prop
->GetChildren() ; 
 409                         if ( elementContent 
) 
 411                             // we skip empty elements 
 412                             if ( elementType
->IsObjectType() ) 
 414                                 int valueId 
= ReadComponent( elementContent 
, callbacks 
) ; 
 415                                 if ( valueId 
!= wxInvalidObjectID 
) 
 417                                     if ( pi
->GetAccessor()->HasAdder() ) 
 418                                         callbacks
->AddToPropertyCollectionAsObject( objectID 
, classInfo 
, pi 
, valueId 
) ; 
 419                                     // TODO for collections we must have a notation on taking over ownership or not 
 420                                     if ( elementType
->GetKind() == wxT_OBJECT 
&& valueId 
!= wxNullObjectID 
) 
 421                                         callbacks
->DestroyObject( valueId 
, GetObjectClassInfo( valueId 
) ) ; 
 426                                 wxxVariant elementValue 
= ReadValue( elementContent 
, elementType 
) ; 
 427                                 if ( pi
->GetAccessor()->HasAdder() ) 
 428                                     callbacks
->AddToPropertyCollection( objectID 
, classInfo 
,pi 
, elementValue 
) ; 
 431                         prop 
= prop
->GetNext() ; 
 434                 else if ( pi
->GetTypeInfo()->IsObjectType() ) 
 436                     // and object can either be streamed out a string or as an object 
 437                     // in case we have no node, then the object's streaming out has been vetoed 
 440                         if ( prop
->GetName() == wxT("object") ) 
 442                             int valueId 
= ReadComponent( prop 
, callbacks 
) ; 
 443                             if ( valueId 
!= wxInvalidObjectID 
) 
 445                                 callbacks
->SetPropertyAsObject( objectID 
, classInfo 
, pi 
, valueId 
) ; 
 446                                 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT 
&& valueId 
!= wxNullObjectID 
) 
 447                                     callbacks
->DestroyObject( valueId 
, GetObjectClassInfo( valueId 
) ) ; 
 452                             wxASSERT( pi
->GetTypeInfo()->HasStringConverters() ) ; 
 453                             wxxVariant nodeval 
= ReadValue( prop 
, pi
->GetTypeInfo() ) ; 
 454                             callbacks
->SetProperty( objectID
, classInfo 
,pi 
, nodeval 
) ; 
 458                 else if ( pi
->GetTypeInfo()->IsDelegateType() ) 
 462                         wxString resstring 
= prop
->GetContent() ; 
 463                         wxInt32 pos 
= resstring
.Find('.') ; 
 464                         if ( pos 
!= wxNOT_FOUND 
) 
 466                             int sinkOid 
= atol(resstring
.Left(pos
).ToAscii()) ; 
 467                             wxString handlerName 
= resstring
.Mid(pos
+1) ; 
 468                             wxClassInfo
* sinkClassInfo 
= GetObjectClassInfo( sinkOid 
) ; 
 470                             callbacks
->SetConnect( objectID 
, classInfo 
, pi 
, sinkClassInfo 
, 
 471                                 sinkClassInfo
->FindHandlerInfo(handlerName
) ,  sinkOid 
) ; 
 475                             wxLogError( _("incorrect event handler string, missing dot") ) ; 
 482                     wxxVariant nodeval 
= ReadValue( prop 
, pi
->GetTypeInfo() ) ; 
 483                     if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG 
) 
 485                         const wxEnumTypeInfo 
*eti 
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ; 
 489                             eti
->ConvertToLong( nodeval 
, realval 
) ; 
 490                             nodeval 
= wxxVariant( realval 
) ; 
 494                             wxLogError( _("Type must have enum - long conversion") ) ; 
 497                     callbacks
->SetProperty( objectID
, classInfo 
,pi 
, nodeval 
) ; 
 503     delete[] createParams 
; 
 504     delete[] createParamOids 
; 
 505     delete[] createClassInfos 
; 
 510 wxxVariant 
wxXmlReader::ReadValue(wxXmlNode 
*node
, 
 511                                   const wxTypeInfo 
*type 
) 
 515         content 
= node
->GetContent() ; 
 517     type
->ConvertFromString( content 
, result 
) ; 
 521 int wxXmlReader::ReadObject( const wxString 
&name 
, wxDepersister 
*callbacks
) 
 523     wxXmlNode 
*iter 
= m_parent
->GetChildren() ; 
 527         if ( iter
->GetPropVal(wxT("name"), &entryName
) ) 
 529             if ( entryName 
== name 
) 
 530                 return ReadComponent( iter
->GetChildren() , callbacks 
) ; 
 532         iter 
= iter
->GetNext() ; 
 534     return wxInvalidObjectID 
;