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
22 #include "wx/object.h"
26 #include "wx/xml/xml.h"
27 #include "wx/tokenzr.h"
28 #include "wx/txtstrm.h"
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
;
537 #endif // wxUSE_EXTENDED_RTTI