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"
32 #include "wx/xtistrm.h"
36 #include "wx/beforestd.h"
40 #include "wx/afterstd.h"
47 // ----------------------------------------------------------------------------
49 // ----------------------------------------------------------------------------
51 // convenience functions
53 void wxXmlAddContentToNode( wxXmlNode
* node
, const wxString
& data
)
55 node
->AddChild(new wxXmlNode(wxXML_TEXT_NODE
, wxT("value"), data
) );
58 wxString
wxXmlGetContentFromNode( wxXmlNode
*node
)
60 if ( node
->GetChildren() )
61 return node
->GetChildren()->GetContent();
66 struct wxObjectXmlWriter::wxObjectXmlWriterInternal
70 vector
< wxXmlNode
* > m_objectStack
;
72 void Push( wxXmlNode
*newCurrent
)
74 m_objectStack
.push_back( m_current
);
75 m_current
= newCurrent
;
80 m_current
= m_objectStack
.back();
81 m_objectStack
.pop_back();
85 wxObjectXmlWriter::wxObjectXmlWriter( wxXmlNode
* rootnode
)
87 m_data
= new wxObjectXmlWriterInternal();
88 m_data
->m_root
= rootnode
;
89 m_data
->m_current
= rootnode
;
92 wxObjectXmlWriter::~wxObjectXmlWriter()
97 void wxObjectXmlWriter::DoBeginWriteTopLevelEntry( const wxString
&name
)
100 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("entry"));
101 pnode
->AddProperty(wxString(wxT("name")), name
);
102 m_data
->m_current
->AddChild(pnode
);
103 m_data
->Push( pnode
);
106 void wxObjectXmlWriter::DoEndWriteTopLevelEntry( const wxString
&WXUNUSED(name
) )
111 void wxObjectXmlWriter::DoBeginWriteObject(const wxObject
*WXUNUSED(object
),
112 const wxClassInfo
*classInfo
,
113 int objectID
, wxVariantBaseArray
&metadata
)
116 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
117 pnode
->AddProperty(wxT("class"), wxString(classInfo
->GetClassName()));
118 pnode
->AddProperty(wxT("id"), wxString::Format( wxT("%d"), objectID
) );
120 for ( size_t i
= 0; i
< metadata
.GetCount(); ++i
)
122 pnode
->AddProperty( metadata
[i
].GetName(), metadata
[i
].GetAsString() );
124 m_data
->m_current
->AddChild(pnode
);
125 m_data
->Push( pnode
);
128 void wxObjectXmlWriter::DoEndWriteObject(const wxObject
*WXUNUSED(object
),
129 const wxClassInfo
*WXUNUSED(classInfo
),
130 int WXUNUSED(objectID
) )
135 void wxObjectXmlWriter::DoWriteSimpleType( wxVariantBase
&value
)
137 wxXmlAddContentToNode( m_data
->m_current
,value
.GetAsString() );
140 void wxObjectXmlWriter::DoBeginWriteElement()
143 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("element") );
144 m_data
->m_current
->AddChild(pnode
);
145 m_data
->Push( pnode
);
148 void wxObjectXmlWriter::DoEndWriteElement()
153 void wxObjectXmlWriter::DoBeginWriteProperty(const wxPropertyInfo
*pi
)
156 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("prop") );
157 pnode
->AddProperty(wxT("name"), pi
->GetName() );
158 m_data
->m_current
->AddChild(pnode
);
159 m_data
->Push( pnode
);
162 void wxObjectXmlWriter::DoEndWriteProperty(const wxPropertyInfo
*WXUNUSED(propInfo
) )
167 void wxObjectXmlWriter::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 void wxObjectXmlWriter::DoWriteNullObject()
178 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
179 m_data
->m_current
->AddChild(pnode
);
182 void wxObjectXmlWriter::DoWriteDelegate( const wxObject
*WXUNUSED(object
),
183 const wxClassInfo
* WXUNUSED(classInfo
),
184 const wxPropertyInfo
*WXUNUSED(pi
),
185 const wxObject
*eventSink
, int sinkObjectID
,
186 const wxClassInfo
* WXUNUSED(eventSinkClassInfo
),
187 const wxHandlerInfo
* handlerInfo
)
189 if ( eventSink
!= NULL
&& handlerInfo
!= NULL
)
191 wxXmlAddContentToNode( m_data
->m_current
,
192 wxString::Format(wxT("%d.%s"), sinkObjectID
, handlerInfo
->GetName().c_str()) );
197 // ----------------------------------------------------------------------------
199 // ----------------------------------------------------------------------------
202 Reading components has not to be extended for components
203 as properties are always sought by typeinfo over all levels
204 and create params are always toplevel class only
207 int wxObjectXmlReader::ReadComponent(wxXmlNode
*node
, wxObjectWriterCallback
*callbacks
)
209 wxASSERT_MSG( callbacks
, wxT("Does not support reading without a Depersistor") );
211 wxClassInfo
*classInfo
;
213 wxVariantBase
*createParams
;
214 int *createParamOids
;
215 const wxClassInfo
** createClassInfos
;
218 wxString ObjectIdString
;
220 children
= node
->GetChildren();
223 // check for a null object or href
224 if (node
->GetPropVal(wxT("href"), &ObjectIdString
) )
226 objectID
= atoi( ObjectIdString
.ToAscii() );
227 if ( HasObjectClassInfo( objectID
) )
233 wxLogError( _("Forward hrefs are not supported") );
234 return wxInvalidObjectID
;
237 if ( !node
->GetPropVal(wxT("id"), &ObjectIdString
) )
239 return wxNullObjectID
;
242 if (!node
->GetPropVal(wxT("class"), &className
))
244 // No class name. Eek. FIXME: error handling
245 return wxInvalidObjectID
;
248 classInfo
= wxClassInfo::FindClass(className
);
249 if ( classInfo
== NULL
)
251 wxLogError( wxString::Format(_("unknown class %s"),className
) );
252 return wxInvalidObjectID
;
255 if ( children
!= NULL
&& children
->GetType() == wxXML_TEXT_NODE
)
257 wxLogError(_("objects cannot have XML Text Nodes") );
258 return wxInvalidObjectID
;
260 if (!node
->GetPropVal(wxT("id"), &ObjectIdString
))
262 wxLogError(_("Objects must have an id attribute") );
263 // No object id. Eek. FIXME: error handling
264 return wxInvalidObjectID
;
266 objectID
= atoi( ObjectIdString
.ToAscii() );
268 // is this object already has been streamed in, return it here
269 if ( HasObjectClassInfo( objectID
) )
271 wxLogError ( wxString::Format(_("Doubly used id : %d"), objectID
) );
272 return wxInvalidObjectID
;
275 // new object, start with allocation
276 // first make the object know to our internal registry
277 SetObjectClassInfo( objectID
, classInfo
);
279 wxVariantBaseArray metadata
;
280 wxXmlProperty
*xp
= node
->GetProperties();
283 if ( xp
->GetName() != wxString(wxT("class")) &&
284 xp
->GetName() != wxString(wxT("id")) )
286 metadata
.Add( new wxVariantBase( xp
->GetValue(), xp
->GetName() ) );
290 if ( !classInfo
->NeedsDirectConstruction() )
291 callbacks
->AllocateObject(objectID
, classInfo
, metadata
);
294 // stream back the Create parameters first
295 createParams
= new wxVariantBase
[ classInfo
->GetCreateParamCount() ];
296 createParamOids
= new int[classInfo
->GetCreateParamCount() ];
297 createClassInfos
= new const wxClassInfo
*[classInfo
->GetCreateParamCount() ];
300 typedef map
<wstring
, wxXmlNode
*> PropertyNodes
;
301 typedef vector
<wstring
> PropertyNames
;
303 typedef map
<string
, wxXmlNode
*> PropertyNodes
;
304 typedef vector
<string
> PropertyNames
;
306 PropertyNodes propertyNodes
;
307 PropertyNames propertyNames
;
312 children
->GetPropVal( wxT("name"), &name
);
313 propertyNames
.push_back( (const wxChar
*)name
.c_str() );
314 propertyNodes
[(const wxChar
*)name
.c_str()] = children
->GetChildren();
315 children
= children
->GetNext();
318 for ( int i
= 0; i
<classInfo
->GetCreateParamCount(); ++i
)
320 const wxChar
* paramName
= classInfo
->GetCreateParamName(i
);
321 PropertyNodes::iterator propiter
= propertyNodes
.find( paramName
);
322 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( paramName
);
325 wxLogError( wxString::Format(_("Unkown Property %s"),paramName
) );
327 // if we don't have the value of a create param set in the xml
328 // we use the default value
329 if ( propiter
!= propertyNodes
.end() )
331 wxXmlNode
* prop
= propiter
->second
;
332 if ( pi
->GetTypeInfo()->IsObjectType() )
334 createParamOids
[i
] = ReadComponent( prop
, callbacks
);
335 createClassInfos
[i
] =
336 wx_dynamic_cast(const wxClassTypeInfo
*, pi
->GetTypeInfo())->GetClassInfo();
340 createParamOids
[i
] = wxInvalidObjectID
;
341 createParams
[i
] = ReadValue( prop
, pi
->GetTypeInfo() );
342 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
344 const wxEnumTypeInfo
*eti
=
345 wx_dynamic_cast(const wxEnumTypeInfo
*, pi
->GetTypeInfo() );
349 eti
->ConvertToLong( createParams
[i
], realval
);
350 createParams
[i
] = wxVariantBase( realval
);
354 wxLogError( _("Type must have enum - long conversion") );
357 createClassInfos
[i
] = NULL
;
360 for ( size_t j
= 0; j
< propertyNames
.size(); ++j
)
362 if ( propertyNames
[j
] == paramName
)
364 propertyNames
[j
] = wxEmptyString
;
371 if ( pi
->GetTypeInfo()->IsObjectType() )
373 createParamOids
[i
] = wxNullObjectID
;
374 createClassInfos
[i
] =
375 wx_dynamic_cast(const wxClassTypeInfo
*, pi
->GetTypeInfo())->GetClassInfo();
379 createParams
[i
] = pi
->GetDefaultValue();
380 createParamOids
[i
] = wxInvalidObjectID
;
385 // got the parameters. Call the Create method
386 if ( classInfo
->NeedsDirectConstruction() )
387 callbacks
->ConstructObject(objectID
, classInfo
,
388 classInfo
->GetCreateParamCount(),
389 createParams
, createParamOids
, createClassInfos
, metadata
);
391 callbacks
->CreateObject(objectID
, classInfo
,
392 classInfo
->GetCreateParamCount(),
393 createParams
, createParamOids
, createClassInfos
, metadata
);
395 // now stream in the rest of the properties, in the sequence their
396 // properties were written in the xml
397 for ( size_t j
= 0; j
< propertyNames
.size(); ++j
)
399 if ( propertyNames
[j
].length() )
401 PropertyNodes::iterator propiter
= propertyNodes
.find( propertyNames
[j
] );
402 if ( propiter
!= propertyNodes
.end() )
404 wxXmlNode
* prop
= propiter
->second
;
405 const wxPropertyInfo
* pi
=
406 classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() );
407 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION
)
409 const wxCollectionTypeInfo
* collType
=
410 wx_dynamic_cast( const wxCollectionTypeInfo
*, pi
->GetTypeInfo() );
411 const wxTypeInfo
* elementType
= collType
->GetElementType();
414 if ( prop
->GetName() != wxT("element") )
416 wxLogError( _("A non empty collection must consist of 'element' nodes" ) );
420 wxXmlNode
* elementContent
= prop
->GetChildren();
421 if ( elementContent
)
423 // we skip empty elements
424 if ( elementType
->IsObjectType() )
426 int valueId
= ReadComponent( elementContent
, callbacks
);
427 if ( valueId
!= wxInvalidObjectID
)
429 if ( pi
->GetAccessor()->HasAdder() )
430 callbacks
->AddToPropertyCollectionAsObject( objectID
, classInfo
, pi
, valueId
);
431 // TODO for collections we must have a notation on taking over ownership or not
432 if ( elementType
->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
433 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) );
438 wxVariantBase elementValue
= ReadValue( elementContent
, elementType
);
439 if ( pi
->GetAccessor()->HasAdder() )
440 callbacks
->AddToPropertyCollection( objectID
, classInfo
,pi
, elementValue
);
443 prop
= prop
->GetNext();
446 else if ( pi
->GetTypeInfo()->IsObjectType() )
448 // and object can either be streamed out a string or as an object
449 // in case we have no node, then the object's streaming out has been vetoed
452 if ( prop
->GetName() == wxT("object") )
454 int valueId
= ReadComponent( prop
, callbacks
);
455 if ( valueId
!= wxInvalidObjectID
)
457 callbacks
->SetPropertyAsObject( objectID
, classInfo
, pi
, valueId
);
458 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
459 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) );
464 wxASSERT( pi
->GetTypeInfo()->HasStringConverters() );
465 wxVariantBase nodeval
= ReadValue( prop
, pi
->GetTypeInfo() );
466 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
);
470 else if ( pi
->GetTypeInfo()->IsDelegateType() )
474 wxString resstring
= prop
->GetContent();
475 wxInt32 pos
= resstring
.Find('.');
476 if ( pos
!= wxNOT_FOUND
)
478 int sinkOid
= atol(resstring
.Left(pos
).ToAscii());
479 wxString handlerName
= resstring
.Mid(pos
+1);
480 wxClassInfo
* sinkClassInfo
= GetObjectClassInfo( sinkOid
);
482 callbacks
->SetConnect( objectID
, classInfo
, pi
, sinkClassInfo
,
483 sinkClassInfo
->FindHandlerInfo(handlerName
), sinkOid
);
487 wxLogError( _("incorrect event handler string, missing dot") );
494 wxVariantBase nodeval
= ReadValue( prop
, pi
->GetTypeInfo() );
495 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
497 const wxEnumTypeInfo
*eti
=
498 wx_dynamic_cast(const wxEnumTypeInfo
*, pi
->GetTypeInfo() );
502 eti
->ConvertToLong( nodeval
, realval
);
503 nodeval
= wxVariantBase( realval
);
507 wxLogError( _("Type must have enum - long conversion") );
510 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
);
516 delete[] createParams
;
517 delete[] createParamOids
;
518 delete[] createClassInfos
;
523 wxVariantBase
wxObjectXmlReader::ReadValue(wxXmlNode
*node
,
524 const wxTypeInfo
*type
)
528 content
= node
->GetContent();
529 wxVariantBase result
;
530 type
->ConvertFromString( content
, result
);
534 int wxObjectXmlReader::ReadObject( const wxString
&name
, wxObjectWriterCallback
*callbacks
)
536 wxXmlNode
*iter
= m_parent
->GetChildren();
540 if ( iter
->GetPropVal(wxT("name"), &entryName
) )
542 if ( entryName
== name
)
543 return ReadComponent( iter
->GetChildren(), callbacks
);
545 iter
= iter
->GetNext();
547 return wxInvalidObjectID
;
550 #endif // wxUSE_EXTENDED_RTTI