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/xtistrm.h"
24 #include "wx/object.h"
29 #include "wx/xml/xml.h"
30 #include "wx/tokenzr.h"
31 #include "wx/txtstrm.h"
32 #include "wx/xtistrm.h"
33 #include "wx/xtixml.h"
37 #include "wx/beforestd.h"
41 #include "wx/afterstd.h"
48 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
52 // convenience functions
54 void wxXmlAddContentToNode( wxXmlNode
* node
, const wxString
& data
)
56 node
->AddChild(new wxXmlNode(wxXML_TEXT_NODE
, wxT("value"), data
) );
59 wxString
wxXmlGetContentFromNode( wxXmlNode
*node
)
61 if ( node
->GetChildren() )
62 return node
->GetChildren()->GetContent();
67 struct wxObjectXmlWriter::wxObjectXmlWriterInternal
71 vector
< wxXmlNode
* > m_objectStack
;
73 void Push( wxXmlNode
*newCurrent
)
75 m_objectStack
.push_back( m_current
);
76 m_current
= newCurrent
;
81 m_current
= m_objectStack
.back();
82 m_objectStack
.pop_back();
86 wxObjectXmlWriter::wxObjectXmlWriter( wxXmlNode
* rootnode
)
88 m_data
= new wxObjectXmlWriterInternal();
89 m_data
->m_root
= rootnode
;
90 m_data
->m_current
= rootnode
;
93 wxObjectXmlWriter::~wxObjectXmlWriter()
98 void wxObjectXmlWriter::DoBeginWriteTopLevelEntry( const wxString
&name
)
101 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("entry"));
102 pnode
->AddProperty(wxString(wxT("name")), name
);
103 m_data
->m_current
->AddChild(pnode
);
104 m_data
->Push( pnode
);
107 void wxObjectXmlWriter::DoEndWriteTopLevelEntry( const wxString
&WXUNUSED(name
) )
112 void wxObjectXmlWriter::DoBeginWriteObject(const wxObject
*WXUNUSED(object
),
113 const wxClassInfo
*classInfo
,
114 int objectID
, const wxStringToAnyHashMap
&metadata
)
117 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
118 pnode
->AddProperty(wxT("class"), wxString(classInfo
->GetClassName()));
119 pnode
->AddProperty(wxT("id"), wxString::Format( wxT("%d"), objectID
) );
121 wxStringToAnyHashMap::const_iterator it
, en
;
122 for( it
= metadata
.begin(), en
= metadata
.end(); it
!= en
; ++it
)
124 pnode
->AddProperty( it
->first
, wxAnyGetAsString(it
->second
) );
127 m_data
->m_current
->AddChild(pnode
);
128 m_data
->Push( pnode
);
131 void wxObjectXmlWriter::DoEndWriteObject(const wxObject
*WXUNUSED(object
),
132 const wxClassInfo
*WXUNUSED(classInfo
),
133 int WXUNUSED(objectID
) )
138 void wxObjectXmlWriter::DoWriteSimpleType( const wxAny
&value
)
140 wxXmlAddContentToNode( m_data
->m_current
,wxAnyGetAsString(value
) );
143 void wxObjectXmlWriter::DoBeginWriteElement()
146 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("element") );
147 m_data
->m_current
->AddChild(pnode
);
148 m_data
->Push( pnode
);
151 void wxObjectXmlWriter::DoEndWriteElement()
156 void wxObjectXmlWriter::DoBeginWriteProperty(const wxPropertyInfo
*pi
)
159 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("prop") );
160 pnode
->AddProperty(wxT("name"), pi
->GetName() );
161 m_data
->m_current
->AddChild(pnode
);
162 m_data
->Push( pnode
);
165 void wxObjectXmlWriter::DoEndWriteProperty(const wxPropertyInfo
*WXUNUSED(propInfo
) )
170 void wxObjectXmlWriter::DoWriteRepeatedObject( int objectID
)
173 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
174 pnode
->AddProperty(wxString(wxT("href")), wxString::Format( wxT("%d"), objectID
) );
175 m_data
->m_current
->AddChild(pnode
);
178 void wxObjectXmlWriter::DoWriteNullObject()
181 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
182 m_data
->m_current
->AddChild(pnode
);
185 void wxObjectXmlWriter::DoWriteDelegate( const wxObject
*WXUNUSED(object
),
186 const wxClassInfo
* WXUNUSED(classInfo
),
187 const wxPropertyInfo
*WXUNUSED(pi
),
188 const wxObject
*eventSink
, int sinkObjectID
,
189 const wxClassInfo
* WXUNUSED(eventSinkClassInfo
),
190 const wxHandlerInfo
* handlerInfo
)
192 if ( eventSink
!= NULL
&& handlerInfo
!= NULL
)
194 wxXmlAddContentToNode( m_data
->m_current
,
195 wxString::Format(wxT("%d.%s"), sinkObjectID
, handlerInfo
->GetName().c_str()) );
200 // ----------------------------------------------------------------------------
202 // ----------------------------------------------------------------------------
205 Reading components has not to be extended for components
206 as properties are always sought by typeinfo over all levels
207 and create params are always toplevel class only
210 int wxObjectXmlReader::ReadComponent(wxXmlNode
*node
, wxObjectReaderCallback
*callbacks
)
212 wxASSERT_MSG( callbacks
, wxT("Does not support reading without a Depersistor") );
214 wxClassInfo
*classInfo
;
217 int *createParamOids
;
218 const wxClassInfo
** createClassInfos
;
221 wxString ObjectIdString
;
223 children
= node
->GetChildren();
226 // check for a null object or href
227 if (node
->GetAttribute(wxT("href"), &ObjectIdString
) )
229 objectID
= atoi( ObjectIdString
.ToAscii() );
230 if ( HasObjectClassInfo( objectID
) )
236 wxLogError( _("Forward hrefs are not supported") );
237 return wxInvalidObjectID
;
240 if ( !node
->GetAttribute(wxT("id"), &ObjectIdString
) )
242 return wxNullObjectID
;
245 if (!node
->GetAttribute(wxT("class"), &className
))
247 // No class name. Eek. FIXME: error handling
248 return wxInvalidObjectID
;
251 classInfo
= wxClassInfo::FindClass(className
);
252 if ( classInfo
== NULL
)
254 wxLogError( wxString::Format(_("unknown class %s"),className
) );
255 return wxInvalidObjectID
;
258 if ( children
!= NULL
&& children
->GetType() == wxXML_TEXT_NODE
)
260 wxLogError(_("objects cannot have XML Text Nodes") );
261 return wxInvalidObjectID
;
263 if (!node
->GetAttribute(wxT("id"), &ObjectIdString
))
265 wxLogError(_("Objects must have an id attribute") );
266 // No object id. Eek. FIXME: error handling
267 return wxInvalidObjectID
;
269 objectID
= atoi( ObjectIdString
.ToAscii() );
271 // is this object already has been streamed in, return it here
272 if ( HasObjectClassInfo( objectID
) )
274 wxLogError ( wxString::Format(_("Doubly used id : %d"), objectID
) );
275 return wxInvalidObjectID
;
278 // new object, start with allocation
279 // first make the object know to our internal registry
280 SetObjectClassInfo( objectID
, classInfo
);
282 wxStringToAnyHashMap metadata
;
283 wxXmlProperty
*xp
= node
->GetAttributes();
286 if ( xp
->GetName() != wxString(wxT("class")) &&
287 xp
->GetName() != wxString(wxT("id")) )
289 metadata
[xp
->GetName()] = wxAny( xp
->GetValue() );
293 if ( !classInfo
->NeedsDirectConstruction() )
294 callbacks
->AllocateObject(objectID
, classInfo
, metadata
);
297 // stream back the Create parameters first
298 createParams
= new wxAny
[ classInfo
->GetCreateParamCount() ];
299 createParamOids
= new int[classInfo
->GetCreateParamCount() ];
300 createClassInfos
= new const wxClassInfo
*[classInfo
->GetCreateParamCount() ];
303 typedef map
<wstring
, wxXmlNode
*> PropertyNodes
;
304 typedef vector
<wstring
> PropertyNames
;
306 typedef map
<string
, wxXmlNode
*> PropertyNodes
;
307 typedef vector
<string
> PropertyNames
;
309 PropertyNodes propertyNodes
;
310 PropertyNames propertyNames
;
315 children
->GetAttribute( wxT("name"), &name
);
316 propertyNames
.push_back( (const wxChar
*)name
.c_str() );
317 propertyNodes
[(const wxChar
*)name
.c_str()] = children
->GetChildren();
318 children
= children
->GetNext();
321 for ( int i
= 0; i
<classInfo
->GetCreateParamCount(); ++i
)
323 const wxChar
* paramName
= classInfo
->GetCreateParamName(i
);
324 PropertyNodes::iterator propiter
= propertyNodes
.find( paramName
);
325 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( paramName
);
328 wxLogError( wxString::Format(_("Unknown Property %s"),paramName
) );
330 // if we don't have the value of a create param set in the xml
331 // we use the default value
332 if ( propiter
!= propertyNodes
.end() )
334 wxXmlNode
* prop
= propiter
->second
;
335 if ( pi
->GetTypeInfo()->IsObjectType() )
337 createParamOids
[i
] = ReadComponent( prop
, callbacks
);
338 createClassInfos
[i
] =
339 wx_dynamic_cast(const wxClassTypeInfo
*, pi
->GetTypeInfo())->GetClassInfo();
343 createParamOids
[i
] = wxInvalidObjectID
;
344 createParams
[i
] = ReadValue( prop
, pi
->GetTypeInfo() );
345 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
347 const wxEnumTypeInfo
*eti
=
348 wx_dynamic_cast(const wxEnumTypeInfo
*, pi
->GetTypeInfo() );
352 eti
->ConvertToLong( createParams
[i
], realval
);
353 createParams
[i
] = wxAny( realval
);
357 wxLogError( _("Type must have enum - long conversion") );
360 createClassInfos
[i
] = NULL
;
363 for ( size_t j
= 0; j
< propertyNames
.size(); ++j
)
365 if ( propertyNames
[j
] == paramName
)
367 propertyNames
[j
] = wxEmptyString
;
374 if ( pi
->GetTypeInfo()->IsObjectType() )
376 createParamOids
[i
] = wxNullObjectID
;
377 createClassInfos
[i
] =
378 wx_dynamic_cast(const wxClassTypeInfo
*, pi
->GetTypeInfo())->GetClassInfo();
382 createParams
[i
] = pi
->GetDefaultValue();
383 createParamOids
[i
] = wxInvalidObjectID
;
388 // got the parameters. Call the Create method
389 if ( classInfo
->NeedsDirectConstruction() )
390 callbacks
->ConstructObject(objectID
, classInfo
,
391 classInfo
->GetCreateParamCount(),
392 createParams
, createParamOids
, createClassInfos
, metadata
);
394 callbacks
->CreateObject(objectID
, classInfo
,
395 classInfo
->GetCreateParamCount(),
396 createParams
, createParamOids
, createClassInfos
, metadata
);
398 // now stream in the rest of the properties, in the sequence their
399 // properties were written in the xml
400 for ( size_t j
= 0; j
< propertyNames
.size(); ++j
)
402 if ( !propertyNames
[j
].empty() )
404 PropertyNodes::iterator propiter
= propertyNodes
.find( propertyNames
[j
] );
405 if ( propiter
!= propertyNodes
.end() )
407 wxXmlNode
* prop
= propiter
->second
;
408 const wxPropertyInfo
* pi
=
409 classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() );
410 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION
)
412 const wxCollectionTypeInfo
* collType
=
413 wx_dynamic_cast( const wxCollectionTypeInfo
*, pi
->GetTypeInfo() );
414 const wxTypeInfo
* elementType
= collType
->GetElementType();
417 if ( prop
->GetName() != wxT("element") )
419 wxLogError( _("A non empty collection must consist of 'element' nodes" ) );
423 wxXmlNode
* elementContent
= prop
->GetChildren();
424 if ( elementContent
)
426 // we skip empty elements
427 if ( elementType
->IsObjectType() )
429 int valueId
= ReadComponent( elementContent
, callbacks
);
430 if ( valueId
!= wxInvalidObjectID
)
432 if ( pi
->GetAccessor()->HasAdder() )
433 callbacks
->AddToPropertyCollectionAsObject( objectID
, classInfo
, pi
, valueId
);
434 // TODO for collections we must have a notation on taking over ownership or not
435 if ( elementType
->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
436 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) );
441 wxAny elementValue
= ReadValue( elementContent
, elementType
);
442 if ( pi
->GetAccessor()->HasAdder() )
443 callbacks
->AddToPropertyCollection( objectID
, classInfo
,pi
, elementValue
);
446 prop
= prop
->GetNext();
449 else if ( pi
->GetTypeInfo()->IsObjectType() )
451 // and object can either be streamed out a string or as an object
452 // in case we have no node, then the object's streaming out has been vetoed
455 if ( prop
->GetName() == wxT("object") )
457 int valueId
= ReadComponent( prop
, callbacks
);
458 if ( valueId
!= wxInvalidObjectID
)
460 callbacks
->SetPropertyAsObject( objectID
, classInfo
, pi
, valueId
);
461 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
462 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) );
467 wxASSERT( pi
->GetTypeInfo()->HasStringConverters() );
468 wxAny nodeval
= ReadValue( prop
, pi
->GetTypeInfo() );
469 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
);
473 else if ( pi
->GetTypeInfo()->IsDelegateType() )
477 wxString resstring
= prop
->GetContent();
478 wxInt32 pos
= resstring
.Find('.');
479 if ( pos
!= wxNOT_FOUND
)
481 int sinkOid
= atol(resstring
.Left(pos
).ToAscii());
482 wxString handlerName
= resstring
.Mid(pos
+1);
483 wxClassInfo
* sinkClassInfo
= GetObjectClassInfo( sinkOid
);
485 callbacks
->SetConnect( objectID
, classInfo
, pi
, sinkClassInfo
,
486 sinkClassInfo
->FindHandlerInfo(handlerName
.c_str()), sinkOid
);
490 wxLogError( _("incorrect event handler string, missing dot") );
497 wxAny nodeval
= ReadValue( prop
, pi
->GetTypeInfo() );
498 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
500 const wxEnumTypeInfo
*eti
=
501 wx_dynamic_cast(const wxEnumTypeInfo
*, pi
->GetTypeInfo() );
505 eti
->ConvertToLong( nodeval
, realval
);
506 nodeval
= wxAny( realval
);
510 wxLogError( _("Type must have enum - long conversion") );
513 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
);
519 delete[] createParams
;
520 delete[] createParamOids
;
521 delete[] createClassInfos
;
526 wxAny
wxObjectXmlReader::ReadValue(wxXmlNode
*node
,
527 const wxTypeInfo
*type
)
531 content
= node
->GetContent();
533 type
->ConvertFromString( content
, result
);
537 int wxObjectXmlReader::ReadObject( const wxString
&name
, wxObjectReaderCallback
*callbacks
)
539 wxXmlNode
*iter
= m_parent
->GetChildren();
543 if ( iter
->GetAttribute(wxT("name"), &entryName
) )
545 if ( entryName
== name
)
546 return ReadComponent( iter
->GetChildren(), callbacks
);
548 iter
= iter
->GetNext();
550 return wxInvalidObjectID
;
553 #endif // wxUSE_EXTENDED_RTTI