wiring OnInit on osx to a later point in event processing
[wxWidgets.git] / src / common / xtixml.cpp
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
43 using namespace std;
44
45
46
47
48 // ----------------------------------------------------------------------------
49 // wxObjectXmlWriter
50 // ----------------------------------------------------------------------------
51
52 // convenience functions
53
54 void wxXmlAddContentToNode( wxXmlNode* node, const wxString& data )
55 {
56 node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, wxT("value"), data ) );
57 }
58
59 wxString wxXmlGetContentFromNode( wxXmlNode *node )
60 {
61 if ( node->GetChildren() )
62 return node->GetChildren()->GetContent();
63 else
64 return wxEmptyString;
65 }
66
67 struct 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
86 wxObjectXmlWriter::wxObjectXmlWriter( wxXmlNode * rootnode )
87 {
88 m_data = new wxObjectXmlWriterInternal();
89 m_data->m_root = rootnode;
90 m_data->m_current = rootnode;
91 }
92
93 wxObjectXmlWriter::~wxObjectXmlWriter()
94 {
95 delete m_data;
96 }
97
98 void 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
107 void wxObjectXmlWriter::DoEndWriteTopLevelEntry( const wxString &WXUNUSED(name) )
108 {
109 m_data->Pop();
110 }
111
112 void 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
131 void wxObjectXmlWriter::DoEndWriteObject(const wxObject *WXUNUSED(object),
132 const wxClassInfo *WXUNUSED(classInfo),
133 int WXUNUSED(objectID) )
134 {
135 m_data->Pop();
136 }
137
138 void wxObjectXmlWriter::DoWriteSimpleType( const wxAny &value )
139 {
140 wxXmlAddContentToNode( m_data->m_current,wxAnyGetAsString(value) );
141 }
142
143 void 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
151 void wxObjectXmlWriter::DoEndWriteElement()
152 {
153 m_data->Pop();
154 }
155
156 void 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
165 void wxObjectXmlWriter::DoEndWriteProperty(const wxPropertyInfo *WXUNUSED(propInfo) )
166 {
167 m_data->Pop();
168 }
169
170 void 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
178 void wxObjectXmlWriter::DoWriteNullObject()
179 {
180 wxXmlNode *pnode;
181 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("object"));
182 m_data->m_current->AddChild(pnode);
183 }
184
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 )
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 /*
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
208 */
209
210 int 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
526 wxAny 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
537 int 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