1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/xtixml.cpp
3 // Purpose: streaming runtime metadata information
4 // Author: Stefan Csomor
7 // Copyright: (c) 2003 Stefan Csomor
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
18 #if wxUSE_EXTENDED_RTTI
20 #include "wx/xtistrm.h"
23 #include "wx/object.h"
28 #include "wx/xml/xml.h"
29 #include "wx/tokenzr.h"
30 #include "wx/txtstrm.h"
31 #include "wx/xtistrm.h"
32 #include "wx/xtixml.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
, const wxStringToAnyHashMap
&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 wxStringToAnyHashMap::const_iterator it
, en
;
121 for( it
= metadata
.begin(), en
= metadata
.end(); it
!= en
; ++it
)
123 pnode
->AddProperty( it
->first
, wxAnyGetAsString(it
->second
) );
126 m_data
->m_current
->AddChild(pnode
);
127 m_data
->Push( pnode
);
130 void wxObjectXmlWriter::DoEndWriteObject(const wxObject
*WXUNUSED(object
),
131 const wxClassInfo
*WXUNUSED(classInfo
),
132 int WXUNUSED(objectID
) )
137 void wxObjectXmlWriter::DoWriteSimpleType( const wxAny
&value
)
139 wxXmlAddContentToNode( m_data
->m_current
,wxAnyGetAsString(value
) );
142 void wxObjectXmlWriter::DoBeginWriteElement()
145 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("element") );
146 m_data
->m_current
->AddChild(pnode
);
147 m_data
->Push( pnode
);
150 void wxObjectXmlWriter::DoEndWriteElement()
155 void wxObjectXmlWriter::DoBeginWriteProperty(const wxPropertyInfo
*pi
)
158 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("prop") );
159 pnode
->AddProperty(wxT("name"), pi
->GetName() );
160 m_data
->m_current
->AddChild(pnode
);
161 m_data
->Push( pnode
);
164 void wxObjectXmlWriter::DoEndWriteProperty(const wxPropertyInfo
*WXUNUSED(propInfo
) )
169 void wxObjectXmlWriter::DoWriteRepeatedObject( int objectID
)
172 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
173 pnode
->AddProperty(wxString(wxT("href")), wxString::Format( wxT("%d"), objectID
) );
174 m_data
->m_current
->AddChild(pnode
);
177 void wxObjectXmlWriter::DoWriteNullObject()
180 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
181 m_data
->m_current
->AddChild(pnode
);
184 void wxObjectXmlWriter::DoWriteDelegate( const wxObject
*WXUNUSED(object
),
185 const wxClassInfo
* WXUNUSED(classInfo
),
186 const wxPropertyInfo
*WXUNUSED(pi
),
187 const wxObject
*eventSink
, int sinkObjectID
,
188 const wxClassInfo
* WXUNUSED(eventSinkClassInfo
),
189 const wxHandlerInfo
* handlerInfo
)
191 if ( eventSink
!= NULL
&& handlerInfo
!= NULL
)
193 wxXmlAddContentToNode( m_data
->m_current
,
194 wxString::Format(wxT("%d.%s"), sinkObjectID
, handlerInfo
->GetName().c_str()) );
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 wxObjectXmlReader::ReadComponent(wxXmlNode
*node
, wxObjectReaderCallback
*callbacks
)
211 wxASSERT_MSG( callbacks
, wxT("Does not support reading without a Depersistor") );
213 wxClassInfo
*classInfo
;
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
->GetAttribute(wxT("href"), &ObjectIdString
) )
228 objectID
= atoi( ObjectIdString
.ToAscii() );
229 if ( HasObjectClassInfo( objectID
) )
235 wxLogError( _("Forward hrefs are not supported") );
236 return wxInvalidObjectID
;
239 if ( !node
->GetAttribute(wxT("id"), &ObjectIdString
) )
241 return wxNullObjectID
;
244 if (!node
->GetAttribute(wxT("class"), &className
))
246 // No class name. Eek. FIXME: error handling
247 return wxInvalidObjectID
;
250 classInfo
= wxClassInfo::FindClass(className
);
251 if ( classInfo
== NULL
)
253 wxLogError( wxString::Format(_("unknown class %s"),className
) );
254 return wxInvalidObjectID
;
257 if ( children
!= NULL
&& children
->GetType() == wxXML_TEXT_NODE
)
259 wxLogError(_("objects cannot have XML Text Nodes") );
260 return wxInvalidObjectID
;
262 if (!node
->GetAttribute(wxT("id"), &ObjectIdString
))
264 wxLogError(_("Objects must have an id attribute") );
265 // No object id. Eek. FIXME: error handling
266 return wxInvalidObjectID
;
268 objectID
= atoi( ObjectIdString
.ToAscii() );
270 // is this object already has been streamed in, return it here
271 if ( HasObjectClassInfo( objectID
) )
273 wxLogError ( wxString::Format(_("Doubly used id : %d"), objectID
) );
274 return wxInvalidObjectID
;
277 // new object, start with allocation
278 // first make the object know to our internal registry
279 SetObjectClassInfo( objectID
, classInfo
);
281 wxStringToAnyHashMap metadata
;
282 wxXmlProperty
*xp
= node
->GetAttributes();
285 if ( xp
->GetName() != wxString(wxT("class")) &&
286 xp
->GetName() != wxString(wxT("id")) )
288 metadata
[xp
->GetName()] = wxAny( xp
->GetValue() );
292 if ( !classInfo
->NeedsDirectConstruction() )
293 callbacks
->AllocateObject(objectID
, classInfo
, metadata
);
296 // stream back the Create parameters first
297 createParams
= new wxAny
[ classInfo
->GetCreateParamCount() ];
298 createParamOids
= new int[classInfo
->GetCreateParamCount() ];
299 createClassInfos
= new const wxClassInfo
*[classInfo
->GetCreateParamCount() ];
302 typedef map
<wstring
, wxXmlNode
*> PropertyNodes
;
303 typedef vector
<wstring
> PropertyNames
;
305 typedef map
<string
, wxXmlNode
*> PropertyNodes
;
306 typedef vector
<string
> PropertyNames
;
308 PropertyNodes propertyNodes
;
309 PropertyNames propertyNames
;
314 children
->GetAttribute( wxT("name"), &name
);
315 propertyNames
.push_back( (const wxChar
*)name
.c_str() );
316 propertyNodes
[(const wxChar
*)name
.c_str()] = children
->GetChildren();
317 children
= children
->GetNext();
320 for ( int i
= 0; i
<classInfo
->GetCreateParamCount(); ++i
)
322 const wxChar
* paramName
= classInfo
->GetCreateParamName(i
);
323 PropertyNodes::iterator propiter
= propertyNodes
.find( paramName
);
324 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( paramName
);
327 wxLogError( wxString::Format(_("Unknown Property %s"),paramName
) );
329 // if we don't have the value of a create param set in the xml
330 // we use the default value
331 if ( propiter
!= propertyNodes
.end() )
333 wxXmlNode
* prop
= propiter
->second
;
334 if ( pi
->GetTypeInfo()->IsObjectType() )
336 createParamOids
[i
] = ReadComponent( prop
, callbacks
);
337 createClassInfos
[i
] =
338 wx_dynamic_cast(const wxClassTypeInfo
*, pi
->GetTypeInfo())->GetClassInfo();
342 createParamOids
[i
] = wxInvalidObjectID
;
343 createParams
[i
] = ReadValue( prop
, pi
->GetTypeInfo() );
344 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
346 const wxEnumTypeInfo
*eti
=
347 wx_dynamic_cast(const wxEnumTypeInfo
*, pi
->GetTypeInfo() );
351 eti
->ConvertToLong( createParams
[i
], realval
);
352 createParams
[i
] = wxAny( realval
);
356 wxLogError( _("Type must have enum - long conversion") );
359 createClassInfos
[i
] = NULL
;
362 for ( size_t j
= 0; j
< propertyNames
.size(); ++j
)
364 if ( propertyNames
[j
] == paramName
)
366 propertyNames
[j
] = wxEmptyString
;
373 if ( pi
->GetTypeInfo()->IsObjectType() )
375 createParamOids
[i
] = wxNullObjectID
;
376 createClassInfos
[i
] =
377 wx_dynamic_cast(const wxClassTypeInfo
*, pi
->GetTypeInfo())->GetClassInfo();
381 createParams
[i
] = pi
->GetDefaultValue();
382 createParamOids
[i
] = wxInvalidObjectID
;
387 // got the parameters. Call the Create method
388 if ( classInfo
->NeedsDirectConstruction() )
389 callbacks
->ConstructObject(objectID
, classInfo
,
390 classInfo
->GetCreateParamCount(),
391 createParams
, createParamOids
, createClassInfos
, metadata
);
393 callbacks
->CreateObject(objectID
, classInfo
,
394 classInfo
->GetCreateParamCount(),
395 createParams
, createParamOids
, createClassInfos
, metadata
);
397 // now stream in the rest of the properties, in the sequence their
398 // properties were written in the xml
399 for ( size_t j
= 0; j
< propertyNames
.size(); ++j
)
401 if ( !propertyNames
[j
].empty() )
403 PropertyNodes::iterator propiter
= propertyNodes
.find( propertyNames
[j
] );
404 if ( propiter
!= propertyNodes
.end() )
406 wxXmlNode
* prop
= propiter
->second
;
407 const wxPropertyInfo
* pi
=
408 classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() );
409 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION
)
411 const wxCollectionTypeInfo
* collType
=
412 wx_dynamic_cast( const wxCollectionTypeInfo
*, pi
->GetTypeInfo() );
413 const wxTypeInfo
* elementType
= collType
->GetElementType();
416 if ( prop
->GetName() != wxT("element") )
418 wxLogError( _("A non empty collection must consist of 'element' nodes" ) );
422 wxXmlNode
* elementContent
= prop
->GetChildren();
423 if ( elementContent
)
425 // we skip empty elements
426 if ( elementType
->IsObjectType() )
428 int valueId
= ReadComponent( elementContent
, callbacks
);
429 if ( valueId
!= wxInvalidObjectID
)
431 if ( pi
->GetAccessor()->HasAdder() )
432 callbacks
->AddToPropertyCollectionAsObject( objectID
, classInfo
, pi
, valueId
);
433 // TODO for collections we must have a notation on taking over ownership or not
434 if ( elementType
->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
435 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) );
440 wxAny elementValue
= ReadValue( elementContent
, elementType
);
441 if ( pi
->GetAccessor()->HasAdder() )
442 callbacks
->AddToPropertyCollection( objectID
, classInfo
,pi
, elementValue
);
445 prop
= prop
->GetNext();
448 else if ( pi
->GetTypeInfo()->IsObjectType() )
450 // and object can either be streamed out a string or as an object
451 // in case we have no node, then the object's streaming out has been vetoed
454 if ( prop
->GetName() == wxT("object") )
456 int valueId
= ReadComponent( prop
, callbacks
);
457 if ( valueId
!= wxInvalidObjectID
)
459 callbacks
->SetPropertyAsObject( objectID
, classInfo
, pi
, valueId
);
460 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
461 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) );
466 wxASSERT( pi
->GetTypeInfo()->HasStringConverters() );
467 wxAny nodeval
= ReadValue( prop
, pi
->GetTypeInfo() );
468 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
);
472 else if ( pi
->GetTypeInfo()->IsDelegateType() )
476 wxString resstring
= prop
->GetContent();
477 wxInt32 pos
= resstring
.Find('.');
478 if ( pos
!= wxNOT_FOUND
)
480 int sinkOid
= atol(resstring
.Left(pos
).ToAscii());
481 wxString handlerName
= resstring
.Mid(pos
+1);
482 wxClassInfo
* sinkClassInfo
= GetObjectClassInfo( sinkOid
);
484 callbacks
->SetConnect( objectID
, classInfo
, pi
, sinkClassInfo
,
485 sinkClassInfo
->FindHandlerInfo(handlerName
.c_str()), sinkOid
);
489 wxLogError( _("incorrect event handler string, missing dot") );
496 wxAny nodeval
= ReadValue( prop
, pi
->GetTypeInfo() );
497 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
499 const wxEnumTypeInfo
*eti
=
500 wx_dynamic_cast(const wxEnumTypeInfo
*, pi
->GetTypeInfo() );
504 eti
->ConvertToLong( nodeval
, realval
);
505 nodeval
= wxAny( realval
);
509 wxLogError( _("Type must have enum - long conversion") );
512 callbacks
->SetProperty( objectID
, classInfo
,pi
, nodeval
);
518 delete[] createParams
;
519 delete[] createParamOids
;
520 delete[] createClassInfos
;
525 wxAny
wxObjectXmlReader::ReadValue(wxXmlNode
*node
,
526 const wxTypeInfo
*type
)
530 content
= node
->GetContent();
532 type
->ConvertFromString( content
, result
);
536 int wxObjectXmlReader::ReadObject( const wxString
&name
, wxObjectReaderCallback
*callbacks
)
538 wxXmlNode
*iter
= m_parent
->GetChildren();
542 if ( iter
->GetAttribute(wxT("name"), &entryName
) )
544 if ( entryName
== name
)
545 return ReadComponent( iter
->GetChildren(), callbacks
);
547 iter
= iter
->GetNext();
549 return wxInvalidObjectID
;
552 #endif // wxUSE_EXTENDED_RTTI