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 wxASSERT_MSG( HasObjectClassInfo( objectID
) , wxT("Forward hrefs are not supported") ) ;
232 if ( !node
->GetPropVal(wxT("id") , &ObjectIdString
) )
234 return wxNullObjectID
;
237 if (!node
->GetPropVal(wxT("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(wxT("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
.ToAscii() ) ;
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(wxT("class")) && xp
->GetName() != wxString(wxT("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() ] ;
279 typedef map
<wstring
, wxXmlNode
*> PropertyNodes
;
280 typedef vector
<wstring
> PropertyNames
;
282 typedef map
<string
, wxXmlNode
*> PropertyNodes
;
283 typedef vector
<string
> PropertyNames
;
285 PropertyNodes propertyNodes
;
286 PropertyNames propertyNames
;
291 children
->GetPropVal( wxT("name") , &name
) ;
292 propertyNames
.push_back( name
.c_str() ) ;
293 propertyNodes
[name
.c_str()] = children
->GetChildren() ;
294 children
= children
->GetNext() ;
297 for ( int i
= 0 ; i
<classInfo
->GetCreateParamCount() ; ++i
)
299 const wxChar
* paramName
= classInfo
->GetCreateParamName(i
) ;
300 PropertyNodes::iterator propiter
= propertyNodes
.find( paramName
) ;
301 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( paramName
) ;
302 wxASSERT_MSG(pi
,wxString::Format(wxT("Unkown Property %s"),paramName
) ) ;
303 // if we don't have the value of a create param set in the xml
304 // we use the default value
305 if ( propiter
!= propertyNodes
.end() )
307 wxXmlNode
* prop
= propiter
->second
;
308 if ( pi
->GetTypeInfo()->IsObjectType() )
310 createParamOids
[i
] = ReadComponent( prop
, callbacks
) ;
311 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ;
315 createParamOids
[i
] = wxInvalidObjectID
;
316 createParams
[i
] = ReadValue( prop
, pi
->GetTypeInfo() ) ;
317 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
319 const wxEnumTypeInfo
*eti
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ;
320 wxASSERT_MSG( eti
, wxT("Type must have enum - long conversion") ) ;
323 eti
->ConvertToLong( createParams
[i
] , realval
) ;
324 createParams
[i
] = wxxVariant( realval
) ;
326 createClassInfos
[i
] = NULL
;
329 for ( size_t j
= 0 ; j
< propertyNames
.size() ; ++j
)
331 if ( propertyNames
[j
] == paramName
)
333 propertyNames
[j
] = wxEmptyString
;
340 if ( pi
->GetTypeInfo()->IsObjectType() )
342 createParamOids
[i
] = wxNullObjectID
;
343 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ;
347 createParams
[i
] = pi
->GetDefaultValue() ;
348 createParamOids
[i
] = wxInvalidObjectID
;
353 // got the parameters. Call the Create method
354 if ( classInfo
->NeedsDirectConstruction() )
355 callbacks
->ConstructObject(objectID
, classInfo
,
356 classInfo
->GetCreateParamCount(),
357 createParams
, createParamOids
, createClassInfos
, metadata
);
359 callbacks
->CreateObject(objectID
, classInfo
,
360 classInfo
->GetCreateParamCount(),
361 createParams
, createParamOids
, createClassInfos
, metadata
);
363 // now stream in the rest of the properties, in the sequence their properties were written in the xml
364 for ( size_t j
= 0 ; j
< propertyNames
.size() ; ++j
)
366 if ( propertyNames
[j
].length() )
368 PropertyNodes::iterator propiter
= propertyNodes
.find( propertyNames
[j
] ) ;
369 if ( propiter
!= propertyNodes
.end() )
371 wxXmlNode
* prop
= propiter
->second
;
372 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() ) ;
373 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION
)
375 const wxCollectionTypeInfo
* collType
= dynamic_cast< const wxCollectionTypeInfo
* >( pi
->GetTypeInfo() ) ;
376 const wxTypeInfo
* elementType
= collType
->GetElementType() ;
379 wxASSERT_MSG(prop
->GetName() == wxT("element") , wxT("A non empty collection must consist of 'element' nodes")) ;
380 wxXmlNode
* elementContent
= prop
->GetChildren() ;
381 if ( elementContent
)
383 // we skip empty elements
384 if ( elementType
->IsObjectType() )
386 int valueId
= ReadComponent( elementContent
, callbacks
) ;
387 if ( valueId
!= wxInvalidObjectID
)
389 if ( pi
->GetAccessor()->HasAdder() )
390 callbacks
->AddToPropertyCollectionAsObject( objectID
, classInfo
, pi
, valueId
) ;
391 // TODO for collections we must have a notation on taking over ownership or not
392 if ( elementType
->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
393 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) ) ;
398 wxxVariant elementValue
= ReadValue( elementContent
, elementType
) ;
399 if ( pi
->GetAccessor()->HasAdder() )
400 callbacks
->AddToPropertyCollection( objectID
, classInfo
,pi
, elementValue
) ;
403 prop
= prop
->GetNext() ;
406 else if ( pi
->GetTypeInfo()->IsObjectType() )
408 // and object can either be streamed out a string or as an object
409 // in case we have no node, then the object's streaming out has been vetoed
412 if ( prop
->GetName() == wxT("object") )
414 int valueId
= ReadComponent( prop
, callbacks
) ;
415 if ( valueId
!= wxInvalidObjectID
)
417 callbacks
->SetPropertyAsObject( objectID
, classInfo
, pi
, valueId
) ;
418 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
419 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) ) ;
424 wxASSERT( pi
->GetTypeInfo()->HasStringConverters() ) ;
425 wxxVariant nodeval
= ReadValue( prop
, pi
->GetTypeInfo() ) ;
426 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
) ;
430 else if ( pi
->GetTypeInfo()->IsDelegateType() )
434 wxString resstring
= prop
->GetContent() ;
435 wxInt32 pos
= resstring
.Find('.') ;
436 assert( pos
!= wxNOT_FOUND
) ;
437 int sinkOid
= atol(resstring
.Left(pos
).ToAscii()) ;
438 wxString handlerName
= resstring
.Mid(pos
+1) ;
439 wxClassInfo
* sinkClassInfo
= GetObjectClassInfo( sinkOid
) ;
441 callbacks
->SetConnect( objectID
, classInfo
, dynamic_cast<const wxDelegateTypeInfo
*>(pi
->GetTypeInfo()) , sinkClassInfo
,
442 sinkClassInfo
->FindHandlerInfo(handlerName
) , sinkOid
) ;
448 wxxVariant nodeval
= ReadValue( prop
, pi
->GetTypeInfo() ) ;
449 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
451 const wxEnumTypeInfo
*eti
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ;
452 wxASSERT_MSG( eti
, wxT("Type must have enum - long conversion") ) ;
455 eti
->ConvertToLong( nodeval
, realval
) ;
456 nodeval
= wxxVariant( realval
) ;
458 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
) ;
464 delete[] createParams
;
465 delete[] createParamOids
;
466 delete[] createClassInfos
;
471 wxxVariant
wxXmlReader::ReadValue(wxXmlNode
*node
,
472 const wxTypeInfo
*type
)
476 content
= node
->GetContent() ;
478 type
->ConvertFromString( content
, result
) ;
482 int wxXmlReader::ReadObject( const wxString
&name
, wxDepersister
*callbacks
)
484 wxXmlNode
*iter
= m_parent
->GetChildren() ;
488 if ( iter
->GetPropVal(wxT("name"), &entryName
) )
490 if ( entryName
== name
)
491 return ReadComponent( iter
->GetChildren() , callbacks
) ;
493 iter
= iter
->GetNext() ;
495 return wxInvalidObjectID
;