1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/xtixml.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"
19 #if wxUSE_EXTENDED_RTTI
21 #include "wx/xtixml.h"
24 #include "wx/object.h"
29 #include "wx/xml/xml.h"
30 #include "wx/tokenzr.h"
31 #include "wx/txtstrm.h"
33 #include "wx/xtistrm.h"
35 #include "wx/beforestd.h"
39 #include "wx/afterstd.h"
47 // convenience functions
49 void wxXmlAddContentToNode( wxXmlNode
* node
, const wxString
& data
)
51 node
->AddChild(new wxXmlNode(wxXML_TEXT_NODE
, wxT("value"), data
) );
54 wxString
wxXmlGetContentFromNode( wxXmlNode
*node
)
56 if ( node
->GetChildren() )
57 return node
->GetChildren()->GetContent() ;
59 return wxEmptyString
;
62 struct wxXmlWriter::wxXmlWriterInternal
65 wxXmlNode
*m_current
;
66 vector
< wxXmlNode
* > m_objectStack
;
68 void Push( wxXmlNode
*newCurrent
)
70 m_objectStack
.push_back( m_current
) ;
71 m_current
= newCurrent
;
76 m_current
= m_objectStack
.back() ;
77 m_objectStack
.pop_back() ;
81 wxXmlWriter::wxXmlWriter( wxXmlNode
* rootnode
)
83 m_data
= new wxXmlWriterInternal() ;
84 m_data
->m_root
= rootnode
;
85 m_data
->m_current
= rootnode
;
88 wxXmlWriter::~wxXmlWriter()
93 void wxXmlWriter::DoBeginWriteTopLevelEntry( const wxString
&name
)
96 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("entry"));
97 pnode
->AddAttribute(wxString(wxT("name")), name
);
98 m_data
->m_current
->AddChild(pnode
) ;
99 m_data
->Push( pnode
) ;
102 void wxXmlWriter::DoEndWriteTopLevelEntry( const wxString
&WXUNUSED(name
) )
107 void wxXmlWriter::DoBeginWriteObject(const wxObject
*WXUNUSED(object
), const wxClassInfo
*classInfo
, int objectID
, wxxVariantArray
&metadata
)
110 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
111 pnode
->AddAttribute(wxT("class"), wxString(classInfo
->GetClassName()));
112 pnode
->AddAttribute(wxT("id"), wxString::Format( wxT("%d") , objectID
) );
114 for ( size_t i
= 0 ; i
< metadata
.GetCount() ; ++i
)
116 pnode
->AddAttribute( metadata
[i
].GetName() , metadata
[i
].GetAsString() ) ;
118 m_data
->m_current
->AddChild(pnode
) ;
119 m_data
->Push( pnode
) ;
122 // end of writing the root object
123 void wxXmlWriter::DoEndWriteObject(const wxObject
*WXUNUSED(object
), const wxClassInfo
*WXUNUSED(classInfo
), int WXUNUSED(objectID
) )
128 // writes a property in the stream format
129 void wxXmlWriter::DoWriteSimpleType( wxxVariant
&value
)
131 wxXmlAddContentToNode( m_data
->m_current
,value
.GetAsString() ) ;
134 void wxXmlWriter::DoBeginWriteElement()
137 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("element") );
138 m_data
->m_current
->AddChild(pnode
) ;
139 m_data
->Push( pnode
) ;
142 void wxXmlWriter::DoEndWriteElement()
147 void wxXmlWriter::DoBeginWriteProperty(const wxPropertyInfo
*pi
)
150 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("prop") );
151 pnode
->AddAttribute(wxT("name"), pi
->GetName() );
152 m_data
->m_current
->AddChild(pnode
) ;
153 m_data
->Push( pnode
) ;
156 void wxXmlWriter::DoEndWriteProperty(const wxPropertyInfo
*WXUNUSED(propInfo
) )
163 // insert an object reference to an already written object
164 void wxXmlWriter::DoWriteRepeatedObject( int objectID
)
167 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
168 pnode
->AddAttribute(wxString(wxT("href")), wxString::Format( wxT("%d") , objectID
) );
169 m_data
->m_current
->AddChild(pnode
) ;
172 // insert a null reference
173 void wxXmlWriter::DoWriteNullObject()
176 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
177 m_data
->m_current
->AddChild(pnode
) ;
180 // writes a delegate in the stream format
181 void wxXmlWriter::DoWriteDelegate( const wxObject
*WXUNUSED(object
), const wxClassInfo
* WXUNUSED(classInfo
) , const wxPropertyInfo
*WXUNUSED(pi
) ,
182 const wxObject
*eventSink
, int sinkObjectID
, const wxClassInfo
* WXUNUSED(eventSinkClassInfo
) , const wxHandlerInfo
* handlerInfo
)
184 if ( eventSink
!= NULL
&& handlerInfo
!= NULL
)
186 wxXmlAddContentToNode( m_data
->m_current
,wxString::Format(wxT("%d.%s"), sinkObjectID
, handlerInfo
->GetName().c_str()) ) ;
190 // ----------------------------------------------------------------------------
191 // reading objects in
192 // ----------------------------------------------------------------------------
196 // ----------------------------------------------------------------------------
198 // ----------------------------------------------------------------------------
201 Reading components has not to be extended for components
202 as properties are always sought by typeinfo over all levels
203 and create params are always toplevel class only
206 int wxXmlReader::ReadComponent(wxXmlNode
*node
, wxDepersister
*callbacks
)
208 wxASSERT_MSG( callbacks
, wxT("Does not support reading without a Depersistor") ) ;
210 wxClassInfo
*classInfo
;
212 wxxVariant
*createParams
;
213 int *createParamOids
;
214 const wxClassInfo
** createClassInfos
;
217 wxString ObjectIdString
;
219 children
= node
->GetChildren();
222 // check for a null object or href
223 if (node
->GetAttribute(wxT("href") , &ObjectIdString
) )
225 objectID
= atoi( ObjectIdString
.ToAscii() ) ;
226 if ( HasObjectClassInfo( objectID
) )
232 wxLogError( _("Forward hrefs are not supported") ) ;
233 return wxInvalidObjectID
;
236 if ( !node
->GetAttribute(wxT("id") , &ObjectIdString
) )
238 return wxNullObjectID
;
241 if (!node
->GetAttribute(wxT("class"), &className
))
243 // No class name. Eek. FIXME: error handling
244 return wxInvalidObjectID
;
246 classInfo
= wxClassInfo::FindClass(className
);
247 if ( classInfo
== NULL
)
249 wxLogError( wxString::Format(_("unknown class %s"),className
) ) ;
250 return wxInvalidObjectID
;
253 if ( children
!= NULL
&& children
->GetType() == wxXML_TEXT_NODE
)
255 wxLogError(_("objects cannot have XML Text Nodes") ) ;
256 return wxInvalidObjectID
;
258 if (!node
->GetAttribute(wxT("id"), &ObjectIdString
))
260 wxLogError(_("Objects must have an id attribute") ) ;
261 // No object id. Eek. FIXME: error handling
262 return wxInvalidObjectID
;
264 objectID
= atoi( ObjectIdString
.ToAscii() ) ;
265 // is this object already has been streamed in, return it here
266 if ( HasObjectClassInfo( objectID
) )
268 wxLogError ( wxString::Format(_("Doubly used id : %d"), objectID
) ) ;
269 return wxInvalidObjectID
;
272 // new object, start with allocation
273 // first make the object know to our internal registry
274 SetObjectClassInfo( objectID
, classInfo
) ;
276 wxxVariantArray metadata
;
277 wxXmlAttribute
*xp
= node
->GetAttributes() ;
280 if ( xp
->GetName() != wxString(wxT("class")) && xp
->GetName() != wxString(wxT("id")) )
282 metadata
.Add( new wxxVariant( xp
->GetValue() , xp
->GetName() ) ) ;
286 if ( !classInfo
->NeedsDirectConstruction() )
287 callbacks
->AllocateObject(objectID
, classInfo
, metadata
);
290 // stream back the Create parameters first
291 createParams
= new wxxVariant
[ classInfo
->GetCreateParamCount() ] ;
292 createParamOids
= new int[classInfo
->GetCreateParamCount() ] ;
293 createClassInfos
= new const wxClassInfo
*[classInfo
->GetCreateParamCount() ] ;
296 typedef map
<wstring
, wxXmlNode
*> PropertyNodes
;
297 typedef vector
<wstring
> PropertyNames
;
299 typedef map
<string
, wxXmlNode
*> PropertyNodes
;
300 typedef vector
<string
> PropertyNames
;
302 PropertyNodes propertyNodes
;
303 PropertyNames propertyNames
;
308 children
->GetAttribute( wxT("name") , &name
) ;
309 propertyNames
.push_back( (const wxChar
*)name
.c_str() ) ;
310 propertyNodes
[(const wxChar
*)name
.c_str()] = children
->GetChildren() ;
311 children
= children
->GetNext() ;
314 for ( int i
= 0 ; i
<classInfo
->GetCreateParamCount() ; ++i
)
316 const wxChar
* paramName
= classInfo
->GetCreateParamName(i
) ;
317 PropertyNodes::iterator propiter
= propertyNodes
.find( paramName
) ;
318 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( paramName
) ;
321 wxLogError( wxString::Format(_("Unkown Property %s"),paramName
) ) ;
323 // if we don't have the value of a create param set in the xml
324 // we use the default value
325 if ( propiter
!= propertyNodes
.end() )
327 wxXmlNode
* prop
= propiter
->second
;
328 if ( pi
->GetTypeInfo()->IsObjectType() )
330 createParamOids
[i
] = ReadComponent( prop
, callbacks
) ;
331 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ;
335 createParamOids
[i
] = wxInvalidObjectID
;
336 createParams
[i
] = ReadValue( prop
, pi
->GetTypeInfo() ) ;
337 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
339 const wxEnumTypeInfo
*eti
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ;
343 eti
->ConvertToLong( createParams
[i
] , realval
) ;
344 createParams
[i
] = wxxVariant( realval
) ;
348 wxLogError( _("Type must have enum - long conversion") ) ;
351 createClassInfos
[i
] = NULL
;
354 for ( size_t j
= 0 ; j
< propertyNames
.size() ; ++j
)
356 if ( propertyNames
[j
] == paramName
)
358 propertyNames
[j
] = wxEmptyString
;
365 if ( pi
->GetTypeInfo()->IsObjectType() )
367 createParamOids
[i
] = wxNullObjectID
;
368 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ;
372 createParams
[i
] = pi
->GetDefaultValue() ;
373 createParamOids
[i
] = wxInvalidObjectID
;
378 // got the parameters. Call the Create method
379 if ( classInfo
->NeedsDirectConstruction() )
380 callbacks
->ConstructObject(objectID
, classInfo
,
381 classInfo
->GetCreateParamCount(),
382 createParams
, createParamOids
, createClassInfos
, metadata
);
384 callbacks
->CreateObject(objectID
, classInfo
,
385 classInfo
->GetCreateParamCount(),
386 createParams
, createParamOids
, createClassInfos
, metadata
);
388 // now stream in the rest of the properties, in the sequence their properties were written in the xml
389 for ( size_t j
= 0 ; j
< propertyNames
.size() ; ++j
)
391 if ( propertyNames
[j
].length() )
393 PropertyNodes::iterator propiter
= propertyNodes
.find( propertyNames
[j
] ) ;
394 if ( propiter
!= propertyNodes
.end() )
396 wxXmlNode
* prop
= propiter
->second
;
397 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() ) ;
398 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION
)
400 const wxCollectionTypeInfo
* collType
= dynamic_cast< const wxCollectionTypeInfo
* >( pi
->GetTypeInfo() ) ;
401 const wxTypeInfo
* elementType
= collType
->GetElementType() ;
404 if ( prop
->GetName() != wxT("element") )
406 wxLogError( _("A non empty collection must consist of 'element' nodes" ) ) ;
409 wxXmlNode
* elementContent
= prop
->GetChildren() ;
410 if ( elementContent
)
412 // we skip empty elements
413 if ( elementType
->IsObjectType() )
415 int valueId
= ReadComponent( elementContent
, callbacks
) ;
416 if ( valueId
!= wxInvalidObjectID
)
418 if ( pi
->GetAccessor()->HasAdder() )
419 callbacks
->AddToPropertyCollectionAsObject( objectID
, classInfo
, pi
, valueId
) ;
420 // TODO for collections we must have a notation on taking over ownership or not
421 if ( elementType
->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
422 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) ) ;
427 wxxVariant elementValue
= ReadValue( elementContent
, elementType
) ;
428 if ( pi
->GetAccessor()->HasAdder() )
429 callbacks
->AddToPropertyCollection( objectID
, classInfo
,pi
, elementValue
) ;
432 prop
= prop
->GetNext() ;
435 else if ( pi
->GetTypeInfo()->IsObjectType() )
437 // and object can either be streamed out a string or as an object
438 // in case we have no node, then the object's streaming out has been vetoed
441 if ( prop
->GetName() == wxT("object") )
443 int valueId
= ReadComponent( prop
, callbacks
) ;
444 if ( valueId
!= wxInvalidObjectID
)
446 callbacks
->SetPropertyAsObject( objectID
, classInfo
, pi
, valueId
) ;
447 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
448 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) ) ;
453 wxASSERT( pi
->GetTypeInfo()->HasStringConverters() ) ;
454 wxxVariant nodeval
= ReadValue( prop
, pi
->GetTypeInfo() ) ;
455 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
) ;
459 else if ( pi
->GetTypeInfo()->IsDelegateType() )
463 wxString resstring
= prop
->GetContent() ;
464 wxInt32 pos
= resstring
.Find('.') ;
465 if ( pos
!= wxNOT_FOUND
)
467 int sinkOid
= atol(resstring
.Left(pos
).ToAscii()) ;
468 wxString handlerName
= resstring
.Mid(pos
+1) ;
469 wxClassInfo
* sinkClassInfo
= GetObjectClassInfo( sinkOid
) ;
471 callbacks
->SetConnect( objectID
, classInfo
, pi
, sinkClassInfo
,
472 sinkClassInfo
->FindHandlerInfo(handlerName
) , sinkOid
) ;
476 wxLogError( _("incorrect event handler string, missing dot") ) ;
483 wxxVariant nodeval
= ReadValue( prop
, pi
->GetTypeInfo() ) ;
484 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
486 const wxEnumTypeInfo
*eti
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ;
490 eti
->ConvertToLong( nodeval
, realval
) ;
491 nodeval
= wxxVariant( realval
) ;
495 wxLogError( _("Type must have enum - long conversion") ) ;
498 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
) ;
504 delete[] createParams
;
505 delete[] createParamOids
;
506 delete[] createClassInfos
;
511 wxxVariant
wxXmlReader::ReadValue(wxXmlNode
*node
,
512 const wxTypeInfo
*type
)
516 content
= node
->GetContent() ;
518 type
->ConvertFromString( content
, result
) ;
522 int wxXmlReader::ReadObject( const wxString
&name
, wxDepersister
*callbacks
)
524 wxXmlNode
*iter
= m_parent
->GetChildren() ;
528 if ( iter
->GetAttribute(wxT("name"), &entryName
) )
530 if ( entryName
== name
)
531 return ReadComponent( iter
->GetChildren() , callbacks
) ;
533 iter
= iter
->GetNext() ;
535 return wxInvalidObjectID
;
538 #endif // wxUSE_EXTENDED_RTTI