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
;