]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/xtixml.cpp
Don't use "Cancel" button in the about dialog of the listctrl sample.
[wxWidgets.git] / src / common / xtixml.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/common/xtixml.cpp
3// Purpose: streaming runtime metadata information
4// Author: Stefan Csomor
5// Modified by:
6// Created: 27/07/03
7// RCS-ID: $Id$
8// Copyright: (c) 2003 Stefan Csomor
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16#pragma hdrstop
17#endif
18
19#if wxUSE_EXTENDED_RTTI
20
21#include "wx/xtistrm.h"
22
23#ifndef WX_PRECOMP
24 #include "wx/object.h"
25 #include "wx/hash.h"
26 #include "wx/event.h"
27#endif
28
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"
34
35// STL headers
36
37#include "wx/beforestd.h"
38#include <map>
39#include <vector>
40#include <string>
41#include "wx/afterstd.h"
42
43using namespace std;
44
45
46
47
48// ----------------------------------------------------------------------------
49// wxObjectXmlWriter
50// ----------------------------------------------------------------------------
51
52// convenience functions
53
54void wxXmlAddContentToNode( wxXmlNode* node, const wxString& data )
55{
56 node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, wxT("value"), data ) );
57}
58
59wxString wxXmlGetContentFromNode( wxXmlNode *node )
60{
61 if ( node->GetChildren() )
62 return node->GetChildren()->GetContent();
63 else
64 return wxEmptyString;
65}
66
67struct wxObjectXmlWriter::wxObjectXmlWriterInternal
68{
69 wxXmlNode *m_root;
70 wxXmlNode *m_current;
71 vector< wxXmlNode * > m_objectStack;
72
73 void Push( wxXmlNode *newCurrent )
74 {
75 m_objectStack.push_back( m_current );
76 m_current = newCurrent;
77 }
78
79 void Pop()
80 {
81 m_current = m_objectStack.back();
82 m_objectStack.pop_back();
83 }
84};
85
86wxObjectXmlWriter::wxObjectXmlWriter( wxXmlNode * rootnode )
87{
88 m_data = new wxObjectXmlWriterInternal();
89 m_data->m_root = rootnode;
90 m_data->m_current = rootnode;
91}
92
93wxObjectXmlWriter::~wxObjectXmlWriter()
94{
95 delete m_data;
96}
97
98void wxObjectXmlWriter::DoBeginWriteTopLevelEntry( const wxString &name )
99{
100 wxXmlNode *pnode;
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 );
105}
106
107void wxObjectXmlWriter::DoEndWriteTopLevelEntry( const wxString &WXUNUSED(name) )
108{
109 m_data->Pop();
110}
111
112void wxObjectXmlWriter::DoBeginWriteObject(const wxObject *WXUNUSED(object),
113 const wxClassInfo *classInfo,
114 int objectID, const wxStringToAnyHashMap &metadata )
115{
116 wxXmlNode *pnode;
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 ) );
120
121 wxStringToAnyHashMap::const_iterator it, en;
122 for( it = metadata.begin(), en = metadata.end(); it != en; ++it )
123 {
124 pnode->AddProperty( it->first, wxAnyGetAsString(it->second) );
125 }
126
127 m_data->m_current->AddChild(pnode);
128 m_data->Push( pnode );
129}
130
131void wxObjectXmlWriter::DoEndWriteObject(const wxObject *WXUNUSED(object),
132 const wxClassInfo *WXUNUSED(classInfo),
133 int WXUNUSED(objectID) )
134{
135 m_data->Pop();
136}
137
138void wxObjectXmlWriter::DoWriteSimpleType( const wxAny &value )
139{
140 wxXmlAddContentToNode( m_data->m_current,wxAnyGetAsString(value) );
141}
142
143void wxObjectXmlWriter::DoBeginWriteElement()
144{
145 wxXmlNode *pnode;
146 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("element") );
147 m_data->m_current->AddChild(pnode);
148 m_data->Push( pnode );
149}
150
151void wxObjectXmlWriter::DoEndWriteElement()
152{
153 m_data->Pop();
154}
155
156void wxObjectXmlWriter::DoBeginWriteProperty(const wxPropertyInfo *pi )
157{
158 wxXmlNode *pnode;
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 );
163}
164
165void wxObjectXmlWriter::DoEndWriteProperty(const wxPropertyInfo *WXUNUSED(propInfo) )
166{
167 m_data->Pop();
168}
169
170void wxObjectXmlWriter::DoWriteRepeatedObject( int objectID )
171{
172 wxXmlNode *pnode;
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);
176}
177
178void wxObjectXmlWriter::DoWriteNullObject()
179{
180 wxXmlNode *pnode;
181 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("object"));
182 m_data->m_current->AddChild(pnode);
183}
184
185void 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 )
191{
192 if ( eventSink != NULL && handlerInfo != NULL )
193 {
194 wxXmlAddContentToNode( m_data->m_current,
195 wxString::Format(wxT("%d.%s"), sinkObjectID, handlerInfo->GetName().c_str()) );
196 }
197}
198
199
200// ----------------------------------------------------------------------------
201// wxObjectXmlReader
202// ----------------------------------------------------------------------------
203
204/*
205Reading components has not to be extended for components
206as properties are always sought by typeinfo over all levels
207and create params are always toplevel class only
208*/
209
210int wxObjectXmlReader::ReadComponent(wxXmlNode *node, wxObjectReaderCallback *callbacks)
211{
212 wxASSERT_MSG( callbacks, wxT("Does not support reading without a Depersistor") );
213 wxString className;
214 wxClassInfo *classInfo;
215
216 wxAny *createParams;
217 int *createParamOids;
218 const wxClassInfo** createClassInfos;
219 wxXmlNode *children;
220 int objectID;
221 wxString ObjectIdString;
222
223 children = node->GetChildren();
224 if (!children)
225 {
226 // check for a null object or href
227 if (node->GetAttribute(wxT("href"), &ObjectIdString ) )
228 {
229 objectID = atoi( ObjectIdString.ToAscii() );
230 if ( HasObjectClassInfo( objectID ) )
231 {
232 return objectID;
233 }
234 else
235 {
236 wxLogError( _("Forward hrefs are not supported") );
237 return wxInvalidObjectID;
238 }
239 }
240 if ( !node->GetAttribute(wxT("id"), &ObjectIdString ) )
241 {
242 return wxNullObjectID;
243 }
244 }
245 if (!node->GetAttribute(wxT("class"), &className))
246 {
247 // No class name. Eek. FIXME: error handling
248 return wxInvalidObjectID;
249 }
250
251 classInfo = wxClassInfo::FindClass(className);
252 if ( classInfo == NULL )
253 {
254 wxLogError( wxString::Format(_("unknown class %s"),className ) );
255 return wxInvalidObjectID;
256 }
257
258 if ( children != NULL && children->GetType() == wxXML_TEXT_NODE )
259 {
260 wxLogError(_("objects cannot have XML Text Nodes") );
261 return wxInvalidObjectID;
262 }
263 if (!node->GetAttribute(wxT("id"), &ObjectIdString))
264 {
265 wxLogError(_("Objects must have an id attribute") );
266 // No object id. Eek. FIXME: error handling
267 return wxInvalidObjectID;
268 }
269 objectID = atoi( ObjectIdString.ToAscii() );
270
271 // is this object already has been streamed in, return it here
272 if ( HasObjectClassInfo( objectID ) )
273 {
274 wxLogError ( wxString::Format(_("Doubly used id : %d"), objectID ) );
275 return wxInvalidObjectID;
276 }
277
278 // new object, start with allocation
279 // first make the object know to our internal registry
280 SetObjectClassInfo( objectID, classInfo );
281
282 wxStringToAnyHashMap metadata;
283 wxXmlProperty *xp = node->GetAttributes();
284 while ( xp )
285 {
286 if ( xp->GetName() != wxString(wxT("class")) &&
287 xp->GetName() != wxString(wxT("id")) )
288 {
289 metadata[xp->GetName()] = wxAny( xp->GetValue() );
290 }
291 xp = xp->GetNext();
292 }
293 if ( !classInfo->NeedsDirectConstruction() )
294 callbacks->AllocateObject(objectID, classInfo, metadata);
295
296 //
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() ];
301
302#if wxUSE_UNICODE
303 typedef map<wstring, wxXmlNode *> PropertyNodes;
304 typedef vector<wstring> PropertyNames;
305#else
306 typedef map<string, wxXmlNode *> PropertyNodes;
307 typedef vector<string> PropertyNames;
308#endif
309 PropertyNodes propertyNodes;
310 PropertyNames propertyNames;
311
312 while( children )
313 {
314 wxString name;
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();
319 }
320
321 for ( int i = 0; i <classInfo->GetCreateParamCount(); ++i )
322 {
323 const wxChar* paramName = classInfo->GetCreateParamName(i);
324 PropertyNodes::iterator propiter = propertyNodes.find( paramName );
325 const wxPropertyInfo* pi = classInfo->FindPropertyInfo( paramName );
326 if ( pi == 0 )
327 {
328 wxLogError( wxString::Format(_("Unknown Property %s"),paramName) );
329 }
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() )
333 {
334 wxXmlNode* prop = propiter->second;
335 if ( pi->GetTypeInfo()->IsObjectType() )
336 {
337 createParamOids[i] = ReadComponent( prop, callbacks );
338 createClassInfos[i] =
339 wx_dynamic_cast(const wxClassTypeInfo*, pi->GetTypeInfo())->GetClassInfo();
340 }
341 else
342 {
343 createParamOids[i] = wxInvalidObjectID;
344 createParams[i] = ReadValue( prop, pi->GetTypeInfo() );
345 if( pi->GetFlags() & wxPROP_ENUM_STORE_LONG )
346 {
347 const wxEnumTypeInfo *eti =
348 wx_dynamic_cast(const wxEnumTypeInfo*, pi->GetTypeInfo() );
349 if ( eti )
350 {
351 long realval;
352 eti->ConvertToLong( createParams[i], realval );
353 createParams[i] = wxAny( realval );
354 }
355 else
356 {
357 wxLogError( _("Type must have enum - long conversion") );
358 }
359 }
360 createClassInfos[i] = NULL;
361 }
362
363 for ( size_t j = 0; j < propertyNames.size(); ++j )
364 {
365 if ( propertyNames[j] == paramName )
366 {
367 propertyNames[j] = wxEmptyString;
368 break;
369 }
370 }
371 }
372 else
373 {
374 if ( pi->GetTypeInfo()->IsObjectType() )
375 {
376 createParamOids[i] = wxNullObjectID;
377 createClassInfos[i] =
378 wx_dynamic_cast(const wxClassTypeInfo*, pi->GetTypeInfo())->GetClassInfo();
379 }
380 else
381 {
382 createParams[i] = pi->GetDefaultValue();
383 createParamOids[i] = wxInvalidObjectID;
384 }
385 }
386 }
387
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 );
393 else
394 callbacks->CreateObject(objectID, classInfo,
395 classInfo->GetCreateParamCount(),
396 createParams, createParamOids, createClassInfos, metadata );
397
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 )
401 {
402 if ( !propertyNames[j].empty() )
403 {
404 PropertyNodes::iterator propiter = propertyNodes.find( propertyNames[j] );
405 if ( propiter != propertyNodes.end() )
406 {
407 wxXmlNode* prop = propiter->second;
408 const wxPropertyInfo* pi =
409 classInfo->FindPropertyInfo( propertyNames[j].c_str() );
410 if ( pi->GetTypeInfo()->GetKind() == wxT_COLLECTION )
411 {
412 const wxCollectionTypeInfo* collType =
413 wx_dynamic_cast( const wxCollectionTypeInfo*, pi->GetTypeInfo() );
414 const wxTypeInfo * elementType = collType->GetElementType();
415 while( prop )
416 {
417 if ( prop->GetName() != wxT("element") )
418 {
419 wxLogError( _("A non empty collection must consist of 'element' nodes" ) );
420 break;
421 }
422
423 wxXmlNode* elementContent = prop->GetChildren();
424 if ( elementContent )
425 {
426 // we skip empty elements
427 if ( elementType->IsObjectType() )
428 {
429 int valueId = ReadComponent( elementContent, callbacks );
430 if ( valueId != wxInvalidObjectID )
431 {
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 ) );
437 }
438 }
439 else
440 {
441 wxAny elementValue = ReadValue( elementContent, elementType );
442 if ( pi->GetAccessor()->HasAdder() )
443 callbacks->AddToPropertyCollection( objectID, classInfo,pi, elementValue );
444 }
445 }
446 prop = prop->GetNext();
447 }
448 }
449 else if ( pi->GetTypeInfo()->IsObjectType() )
450 {
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
453 if ( prop )
454 {
455 if ( prop->GetName() == wxT("object") )
456 {
457 int valueId = ReadComponent( prop, callbacks );
458 if ( valueId != wxInvalidObjectID )
459 {
460 callbacks->SetPropertyAsObject( objectID, classInfo, pi, valueId );
461 if ( pi->GetTypeInfo()->GetKind() == wxT_OBJECT && valueId != wxNullObjectID )
462 callbacks->DestroyObject( valueId, GetObjectClassInfo( valueId ) );
463 }
464 }
465 else
466 {
467 wxASSERT( pi->GetTypeInfo()->HasStringConverters() );
468 wxAny nodeval = ReadValue( prop, pi->GetTypeInfo() );
469 callbacks->SetProperty( objectID, classInfo,pi, nodeval );
470 }
471 }
472 }
473 else if ( pi->GetTypeInfo()->IsDelegateType() )
474 {
475 if ( prop )
476 {
477 wxString resstring = prop->GetContent();
478 wxInt32 pos = resstring.Find('.');
479 if ( pos != wxNOT_FOUND )
480 {
481 int sinkOid = atol(resstring.Left(pos).ToAscii());
482 wxString handlerName = resstring.Mid(pos+1);
483 wxClassInfo* sinkClassInfo = GetObjectClassInfo( sinkOid );
484
485 callbacks->SetConnect( objectID, classInfo, pi, sinkClassInfo,
486 sinkClassInfo->FindHandlerInfo(handlerName.c_str()), sinkOid );
487 }
488 else
489 {
490 wxLogError( _("incorrect event handler string, missing dot") );
491 }
492 }
493
494 }
495 else
496 {
497 wxAny nodeval = ReadValue( prop, pi->GetTypeInfo() );
498 if( pi->GetFlags() & wxPROP_ENUM_STORE_LONG )
499 {
500 const wxEnumTypeInfo *eti =
501 wx_dynamic_cast(const wxEnumTypeInfo*, pi->GetTypeInfo() );
502 if ( eti )
503 {
504 long realval;
505 eti->ConvertToLong( nodeval, realval );
506 nodeval = wxAny( realval );
507 }
508 else
509 {
510 wxLogError( _("Type must have enum - long conversion") );
511 }
512 }
513 callbacks->SetProperty( objectID, classInfo,pi, nodeval );
514 }
515 }
516 }
517 }
518
519 delete[] createParams;
520 delete[] createParamOids;
521 delete[] createClassInfos;
522
523 return objectID;
524}
525
526wxAny wxObjectXmlReader::ReadValue(wxXmlNode *node,
527 const wxTypeInfo *type )
528{
529 wxString content;
530 if ( node )
531 content = node->GetContent();
532 wxAny result;
533 type->ConvertFromString( content, result );
534 return result;
535}
536
537int wxObjectXmlReader::ReadObject( const wxString &name, wxObjectReaderCallback *callbacks)
538{
539 wxXmlNode *iter = m_parent->GetChildren();
540 while ( iter )
541 {
542 wxString entryName;
543 if ( iter->GetAttribute(wxT("name"), &entryName) )
544 {
545 if ( entryName == name )
546 return ReadComponent( iter->GetChildren(), callbacks );
547 }
548 iter = iter->GetNext();
549 }
550 return wxInvalidObjectID;
551}
552
553#endif // wxUSE_EXTENDED_RTTI