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
, "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("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( "%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
, "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
, "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("href"), wxString::Format( "%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("href" , &ObjectIdString
) )
228 objectID
= atoi( ObjectIdString
.c_str() ) ;
229 wxASSERT_MSG( HasObjectClassInfo( objectID
) , wxT("Forward hrefs are not supported") ) ;
232 if ( !node
->GetPropVal("id" , &ObjectIdString
) )
234 return wxNullObjectID
;
237 if (!node
->GetPropVal("class", &className
))
239 // No class name. Eek. FIXME: error handling
240 return wxInvalidObjectID
;
242 classInfo
= wxClassInfo::FindClass(className
);
243 wxASSERT_MSG( classInfo
, wxString::Format(wxT("unknown class %s"),className
) ) ;
244 wxASSERT_MSG( !children
|| children
->GetType() != wxXML_TEXT_NODE
, wxT("objects cannot have XML Text Nodes") ) ;
245 if (!node
->GetPropVal("id", &ObjectIdString
))
247 wxASSERT_MSG(0,wxT("Objects must have an id attribute") ) ;
248 // No object id. Eek. FIXME: error handling
249 return wxInvalidObjectID
;
251 objectID
= atoi( ObjectIdString
.c_str() ) ;
252 // is this object already has been streamed in, return it here
253 wxASSERT_MSG( !HasObjectClassInfo( objectID
) , wxString::Format(wxT("Doubly used id : %d"), objectID
) ) ;
255 // new object, start with allocation
256 // first make the object know to our internal registry
257 SetObjectClassInfo( objectID
, classInfo
) ;
259 wxxVariantArray metadata
;
260 wxXmlProperty
*xp
= node
->GetProperties() ;
263 if ( xp
->GetName() != wxString("class") && xp
->GetName() != wxString("id") )
265 metadata
.Add( new wxxVariant( xp
->GetValue() , xp
->GetName() ) ) ;
269 if ( !classInfo
->NeedsDirectConstruction() )
270 callbacks
->AllocateObject(objectID
, classInfo
, metadata
);
273 // stream back the Create parameters first
274 createParams
= new wxxVariant
[ classInfo
->GetCreateParamCount() ] ;
275 createParamOids
= new int[classInfo
->GetCreateParamCount() ] ;
276 createClassInfos
= new const wxClassInfo
*[classInfo
->GetCreateParamCount() ] ;
278 typedef map
<string
, wxXmlNode
*> PropertyNodes
;
279 typedef vector
<string
> PropertyNames
;
281 PropertyNodes propertyNodes
;
282 PropertyNames propertyNames
;
287 children
->GetPropVal( wxT("name") , &name
) ;
288 propertyNames
.push_back( name
.c_str() ) ;
289 propertyNodes
[name
.c_str()] = children
->GetChildren() ;
290 children
= children
->GetNext() ;
293 for ( int i
= 0 ; i
<classInfo
->GetCreateParamCount() ; ++i
)
295 const wxChar
* paramName
= classInfo
->GetCreateParamName(i
) ;
296 PropertyNodes::iterator propiter
= propertyNodes
.find( paramName
) ;
297 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( paramName
) ;
298 wxASSERT_MSG(pi
,wxString::Format("Unkown Property %s",paramName
) ) ;
299 // if we don't have the value of a create param set in the xml
300 // we use the default value
301 if ( propiter
!= propertyNodes
.end() )
303 wxXmlNode
* prop
= propiter
->second
;
304 if ( pi
->GetTypeInfo()->IsObjectType() )
306 createParamOids
[i
] = ReadComponent( prop
, callbacks
) ;
307 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ;
311 createParamOids
[i
] = wxInvalidObjectID
;
312 createParams
[i
] = ReadValue( prop
, pi
->GetTypeInfo() ) ;
313 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
315 const wxEnumTypeInfo
*eti
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ;
316 wxASSERT_MSG( eti
, wxT("Type must have enum - long conversion") ) ;
319 eti
->ConvertToLong( createParams
[i
] , realval
) ;
320 createParams
[i
] = wxxVariant( realval
) ;
322 createClassInfos
[i
] = NULL
;
325 for ( size_t j
= 0 ; j
< propertyNames
.size() ; ++j
)
327 if ( propertyNames
[j
] == paramName
)
329 propertyNames
[j
] = "" ;
336 if ( pi
->GetTypeInfo()->IsObjectType() )
338 createParamOids
[i
] = wxNullObjectID
;
339 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ;
343 createParams
[i
] = pi
->GetDefaultValue() ;
344 createParamOids
[i
] = wxInvalidObjectID
;
349 // got the parameters. Call the Create method
350 if ( classInfo
->NeedsDirectConstruction() )
351 callbacks
->ConstructObject(objectID
, classInfo
,
352 classInfo
->GetCreateParamCount(),
353 createParams
, createParamOids
, createClassInfos
, metadata
);
355 callbacks
->CreateObject(objectID
, classInfo
,
356 classInfo
->GetCreateParamCount(),
357 createParams
, createParamOids
, createClassInfos
, metadata
);
359 // now stream in the rest of the properties, in the sequence their properties were written in the xml
360 for ( size_t j
= 0 ; j
< propertyNames
.size() ; ++j
)
362 if ( propertyNames
[j
].length() )
364 PropertyNodes::iterator propiter
= propertyNodes
.find( propertyNames
[j
] ) ;
365 if ( propiter
!= propertyNodes
.end() )
367 wxXmlNode
* prop
= propiter
->second
;
368 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() ) ;
369 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION
)
371 const wxCollectionTypeInfo
* collType
= dynamic_cast< const wxCollectionTypeInfo
* >( pi
->GetTypeInfo() ) ;
372 const wxTypeInfo
* elementType
= collType
->GetElementType() ;
375 wxASSERT_MSG(prop
->GetName() == wxT("element") , wxT("A non empty collection must consist of 'element' nodes")) ;
376 wxXmlNode
* elementContent
= prop
->GetChildren() ;
377 if ( elementContent
)
379 // we skip empty elements
380 if ( elementType
->IsObjectType() )
382 int valueId
= ReadComponent( elementContent
, callbacks
) ;
383 if ( valueId
!= wxInvalidObjectID
)
385 if ( pi
->GetAccessor()->HasAdder() )
386 callbacks
->AddToPropertyCollectionAsObject( objectID
, classInfo
, pi
, valueId
) ;
387 // TODO for collections we must have a notation on taking over ownership or not
388 if ( elementType
->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
389 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) ) ;
394 wxxVariant elementValue
= ReadValue( elementContent
, elementType
) ;
395 if ( pi
->GetAccessor()->HasAdder() )
396 callbacks
->AddToPropertyCollection( objectID
, classInfo
,pi
, elementValue
) ;
399 prop
= prop
->GetNext() ;
402 else if ( pi
->GetTypeInfo()->IsObjectType() )
404 // and object can either be streamed out a string or as an object
405 // in case we have no node, then the object's streaming out has been vetoed
408 if ( prop
->GetName() == wxT("object") )
410 int valueId
= ReadComponent( prop
, callbacks
) ;
411 if ( valueId
!= wxInvalidObjectID
)
413 callbacks
->SetPropertyAsObject( objectID
, classInfo
, pi
, valueId
) ;
414 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
415 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) ) ;
420 wxASSERT( pi
->GetTypeInfo()->HasStringConverters() ) ;
421 wxxVariant nodeval
= ReadValue( prop
, pi
->GetTypeInfo() ) ;
422 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
) ;
426 else if ( pi
->GetTypeInfo()->IsDelegateType() )
430 wxString resstring
= prop
->GetContent() ;
431 wxInt32 pos
= resstring
.Find('.') ;
432 assert( pos
!= wxNOT_FOUND
) ;
433 int sinkOid
= atol(resstring
.Left(pos
)) ;
434 wxString handlerName
= resstring
.Mid(pos
+1) ;
435 wxClassInfo
* sinkClassInfo
= GetObjectClassInfo( sinkOid
) ;
437 callbacks
->SetConnect( objectID
, classInfo
, dynamic_cast<const wxDelegateTypeInfo
*>(pi
->GetTypeInfo()) , sinkClassInfo
,
438 sinkClassInfo
->FindHandlerInfo(handlerName
) , sinkOid
) ;
444 wxxVariant nodeval
= ReadValue( prop
, pi
->GetTypeInfo() ) ;
445 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
447 const wxEnumTypeInfo
*eti
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ;
448 wxASSERT_MSG( eti
, wxT("Type must have enum - long conversion") ) ;
451 eti
->ConvertToLong( nodeval
, realval
) ;
452 nodeval
= wxxVariant( realval
) ;
454 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
) ;
460 delete[] createParams
;
461 delete[] createParamOids
;
462 delete[] createClassInfos
;
467 wxxVariant
wxXmlReader::ReadValue(wxXmlNode
*node
,
468 const wxTypeInfo
*type
)
472 content
= node
->GetContent() ;
474 type
->ConvertFromString( content
, result
) ;
478 int wxXmlReader::ReadObject( const wxString
&name
, wxDepersister
*callbacks
)
480 wxXmlNode
*iter
= m_parent
->GetChildren() ;
484 if ( iter
->GetPropVal("name", &entryName
) )
486 if ( entryName
== name
)
487 return ReadComponent( iter
->GetChildren() , callbacks
) ;
489 iter
= iter
->GetNext() ;
491 return wxInvalidObjectID
;