]> git.saurik.com Git - wxWidgets.git/blob - src/common/xtixml.cpp
A slightly modified version of Patch #1197468. Keeps track of pending
[wxWidgets.git] / src / common / xtixml.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/xtistrm.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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "xtistrm.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include "wx/hash.h"
25 #include "wx/object.h"
26 #endif
27
28 #include "wx/xml/xml.h"
29 #include "wx/tokenzr.h"
30 #include "wx/txtstrm.h"
31 #include "wx/event.h"
32
33 #if wxUSE_EXTENDED_RTTI
34
35 #include "wx/xtistrm.h"
36 #include "wx/xtixml.h"
37
38 #include "wx/beforestd.h"
39 #include <map>
40 #include <vector>
41 #include <string>
42 #include "wx/afterstd.h"
43
44 using namespace std ;
45
46 //
47 // XML Streaming
48 //
49
50 // convenience functions
51
52 void wxXmlAddContentToNode( wxXmlNode* node , const wxString& data )
53 {
54 node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, wxT("value"), data ) );
55 }
56
57 wxString wxXmlGetContentFromNode( wxXmlNode *node )
58 {
59 if ( node->GetChildren() )
60 return node->GetChildren()->GetContent() ;
61 else
62 return wxEmptyString ;
63 }
64
65 struct wxXmlWriter::wxXmlWriterInternal
66 {
67 wxXmlNode *m_root ;
68 wxXmlNode *m_current ;
69 vector< wxXmlNode * > m_objectStack ;
70
71 void Push( wxXmlNode *newCurrent )
72 {
73 m_objectStack.push_back( m_current ) ;
74 m_current = newCurrent ;
75 }
76
77 void Pop()
78 {
79 m_current = m_objectStack.back() ;
80 m_objectStack.pop_back() ;
81 }
82 } ;
83
84 wxXmlWriter::wxXmlWriter( wxXmlNode * rootnode )
85 {
86 m_data = new wxXmlWriterInternal() ;
87 m_data->m_root = rootnode ;
88 m_data->m_current = rootnode ;
89 }
90
91 wxXmlWriter::~wxXmlWriter()
92 {
93 delete m_data ;
94 }
95
96 void wxXmlWriter::DoBeginWriteTopLevelEntry( const wxString &name )
97 {
98 wxXmlNode *pnode;
99 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("entry"));
100 pnode->AddProperty(wxString(wxT("name")), name);
101 m_data->m_current->AddChild(pnode) ;
102 m_data->Push( pnode ) ;
103 }
104
105 void wxXmlWriter::DoEndWriteTopLevelEntry( const wxString &WXUNUSED(name) )
106 {
107 m_data->Pop() ;
108 }
109
110 void wxXmlWriter::DoBeginWriteObject(const wxObject *WXUNUSED(object), const wxClassInfo *classInfo, int objectID , wxxVariantArray &metadata )
111 {
112 wxXmlNode *pnode;
113 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("object"));
114 pnode->AddProperty(wxT("class"), wxString(classInfo->GetClassName()));
115 pnode->AddProperty(wxT("id"), wxString::Format( wxT("%d") , objectID ) );
116
117 for ( size_t i = 0 ; i < metadata.GetCount() ; ++i )
118 {
119 pnode->AddProperty( metadata[i].GetName() , metadata[i].GetAsString() ) ;
120 }
121 m_data->m_current->AddChild(pnode) ;
122 m_data->Push( pnode ) ;
123 }
124
125 // end of writing the root object
126 void wxXmlWriter::DoEndWriteObject(const wxObject *WXUNUSED(object), const wxClassInfo *WXUNUSED(classInfo), int WXUNUSED(objectID) )
127 {
128 m_data->Pop() ;
129 }
130
131 // writes a property in the stream format
132 void wxXmlWriter::DoWriteSimpleType( wxxVariant &value )
133 {
134 wxXmlAddContentToNode( m_data->m_current ,value.GetAsString() ) ;
135 }
136
137 void wxXmlWriter::DoBeginWriteElement()
138 {
139 wxXmlNode *pnode;
140 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("element") );
141 m_data->m_current->AddChild(pnode) ;
142 m_data->Push( pnode ) ;
143 }
144
145 void wxXmlWriter::DoEndWriteElement()
146 {
147 m_data->Pop() ;
148 }
149
150 void wxXmlWriter::DoBeginWriteProperty(const wxPropertyInfo *pi )
151 {
152 wxXmlNode *pnode;
153 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("prop") );
154 pnode->AddProperty(wxT("name"), pi->GetName() );
155 m_data->m_current->AddChild(pnode) ;
156 m_data->Push( pnode ) ;
157 }
158
159 void wxXmlWriter::DoEndWriteProperty(const wxPropertyInfo *WXUNUSED(propInfo) )
160 {
161 m_data->Pop() ;
162 }
163
164
165
166 // insert an object reference to an already written object
167 void wxXmlWriter::DoWriteRepeatedObject( int objectID )
168 {
169 wxXmlNode *pnode;
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) ;
173 }
174
175 // insert a null reference
176 void wxXmlWriter::DoWriteNullObject()
177 {
178 wxXmlNode *pnode;
179 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("object"));
180 m_data->m_current->AddChild(pnode) ;
181 }
182
183 // writes a delegate in the stream format
184 void wxXmlWriter::DoWriteDelegate( const wxObject *WXUNUSED(object), const wxClassInfo* WXUNUSED(classInfo) , const wxPropertyInfo *WXUNUSED(pi) ,
185 const wxObject *eventSink, int sinkObjectID , const wxClassInfo* WXUNUSED(eventSinkClassInfo) , const wxHandlerInfo* handlerInfo )
186 {
187 if ( eventSink != NULL && handlerInfo != NULL )
188 {
189 wxXmlAddContentToNode( m_data->m_current ,wxString::Format(wxT("%d.%s"), sinkObjectID , handlerInfo->GetName().c_str()) ) ;
190 }
191 }
192
193 // ----------------------------------------------------------------------------
194 // reading objects in
195 // ----------------------------------------------------------------------------
196
197
198
199 // ----------------------------------------------------------------------------
200 // reading xml in
201 // ----------------------------------------------------------------------------
202
203 /*
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
207 */
208
209 int wxXmlReader::ReadComponent(wxXmlNode *node, wxDepersister *callbacks)
210 {
211 wxASSERT_MSG( callbacks , wxT("Does not support reading without a Depersistor") ) ;
212 wxString className;
213 wxClassInfo *classInfo;
214
215 wxxVariant *createParams ;
216 int *createParamOids ;
217 const wxClassInfo** createClassInfos ;
218 wxXmlNode *children;
219 int objectID;
220 wxString ObjectIdString ;
221
222 children = node->GetChildren();
223 if (!children)
224 {
225 // check for a null object or href
226 if (node->GetPropVal(wxT("href") , &ObjectIdString ) )
227 {
228 objectID = atoi( ObjectIdString.ToAscii() ) ;
229 if ( HasObjectClassInfo( objectID ) )
230 {
231 return objectID ;
232 }
233 else
234 {
235 wxLogError( _("Forward hrefs are not supported") ) ;
236 return wxInvalidObjectID ;
237 }
238 }
239 if ( !node->GetPropVal(wxT("id") , &ObjectIdString ) )
240 {
241 return wxNullObjectID;
242 }
243 }
244 if (!node->GetPropVal(wxT("class"), &className))
245 {
246 // No class name. Eek. FIXME: error handling
247 return wxInvalidObjectID;
248 }
249 classInfo = wxClassInfo::FindClass(className);
250 if ( classInfo == NULL )
251 {
252 wxLogError( wxString::Format(_("unknown class %s"),className ) ) ;
253 return wxInvalidObjectID ;
254 }
255
256 if ( children != NULL && children->GetType() == wxXML_TEXT_NODE )
257 {
258 wxLogError(_("objects cannot have XML Text Nodes") ) ;
259 return wxInvalidObjectID;
260 }
261 if (!node->GetPropVal(wxT("id"), &ObjectIdString))
262 {
263 wxLogError(_("Objects must have an id attribute") ) ;
264 // No object id. Eek. FIXME: error handling
265 return wxInvalidObjectID;
266 }
267 objectID = atoi( ObjectIdString.ToAscii() ) ;
268 // is this object already has been streamed in, return it here
269 if ( HasObjectClassInfo( objectID ) )
270 {
271 wxLogError ( wxString::Format(_("Doubly used id : %d"), objectID ) ) ;
272 return wxInvalidObjectID ;
273 }
274
275 // new object, start with allocation
276 // first make the object know to our internal registry
277 SetObjectClassInfo( objectID , classInfo ) ;
278
279 wxxVariantArray metadata ;
280 wxXmlProperty *xp = node->GetProperties() ;
281 while ( xp )
282 {
283 if ( xp->GetName() != wxString(wxT("class")) && xp->GetName() != wxString(wxT("id")) )
284 {
285 metadata.Add( new wxxVariant( xp->GetValue() , xp->GetName() ) ) ;
286 }
287 xp = xp->GetNext() ;
288 }
289 if ( !classInfo->NeedsDirectConstruction() )
290 callbacks->AllocateObject(objectID, classInfo, metadata);
291
292 //
293 // stream back the Create parameters first
294 createParams = new wxxVariant[ classInfo->GetCreateParamCount() ] ;
295 createParamOids = new int[classInfo->GetCreateParamCount() ] ;
296 createClassInfos = new const wxClassInfo*[classInfo->GetCreateParamCount() ] ;
297
298 #if wxUSE_UNICODE
299 typedef map<wstring, wxXmlNode *> PropertyNodes ;
300 typedef vector<wstring> PropertyNames ;
301 #else
302 typedef map<string, wxXmlNode *> PropertyNodes ;
303 typedef vector<string> PropertyNames ;
304 #endif
305 PropertyNodes propertyNodes ;
306 PropertyNames propertyNames ;
307
308 while( children )
309 {
310 wxString name ;
311 children->GetPropVal( wxT("name") , &name ) ;
312 propertyNames.push_back( name.c_str() ) ;
313 propertyNodes[name.c_str()] = children->GetChildren() ;
314 children = children->GetNext() ;
315 }
316
317 for ( int i = 0 ; i <classInfo->GetCreateParamCount() ; ++i )
318 {
319 const wxChar* paramName = classInfo->GetCreateParamName(i) ;
320 PropertyNodes::iterator propiter = propertyNodes.find( paramName ) ;
321 const wxPropertyInfo* pi = classInfo->FindPropertyInfo( paramName ) ;
322 if ( pi == 0 )
323 {
324 wxLogError( wxString::Format(_("Unkown Property %s"),paramName) ) ;
325 }
326 // if we don't have the value of a create param set in the xml
327 // we use the default value
328 if ( propiter != propertyNodes.end() )
329 {
330 wxXmlNode* prop = propiter->second ;
331 if ( pi->GetTypeInfo()->IsObjectType() )
332 {
333 createParamOids[i] = ReadComponent( prop , callbacks ) ;
334 createClassInfos[i] = dynamic_cast<const wxClassTypeInfo*>(pi->GetTypeInfo())->GetClassInfo() ;
335 }
336 else
337 {
338 createParamOids[i] = wxInvalidObjectID ;
339 createParams[i] = ReadValue( prop , pi->GetTypeInfo() ) ;
340 if( pi->GetFlags() & wxPROP_ENUM_STORE_LONG )
341 {
342 const wxEnumTypeInfo *eti = dynamic_cast<const wxEnumTypeInfo*>( pi->GetTypeInfo() ) ;
343 if ( eti )
344 {
345 long realval ;
346 eti->ConvertToLong( createParams[i] , realval ) ;
347 createParams[i] = wxxVariant( realval ) ;
348 }
349 else
350 {
351 wxLogError( _("Type must have enum - long conversion") ) ;
352 }
353 }
354 createClassInfos[i] = NULL ;
355 }
356
357 for ( size_t j = 0 ; j < propertyNames.size() ; ++j )
358 {
359 if ( propertyNames[j] == paramName )
360 {
361 propertyNames[j] = wxEmptyString ;
362 break ;
363 }
364 }
365 }
366 else
367 {
368 if ( pi->GetTypeInfo()->IsObjectType() )
369 {
370 createParamOids[i] = wxNullObjectID ;
371 createClassInfos[i] = dynamic_cast<const wxClassTypeInfo*>(pi->GetTypeInfo())->GetClassInfo() ;
372 }
373 else
374 {
375 createParams[i] = pi->GetDefaultValue() ;
376 createParamOids[i] = wxInvalidObjectID ;
377 }
378 }
379 }
380
381 // got the parameters. Call the Create method
382 if ( classInfo->NeedsDirectConstruction() )
383 callbacks->ConstructObject(objectID, classInfo,
384 classInfo->GetCreateParamCount(),
385 createParams, createParamOids, createClassInfos, metadata );
386 else
387 callbacks->CreateObject(objectID, classInfo,
388 classInfo->GetCreateParamCount(),
389 createParams, createParamOids, createClassInfos, metadata );
390
391 // now stream in the rest of the properties, in the sequence their properties were written in the xml
392 for ( size_t j = 0 ; j < propertyNames.size() ; ++j )
393 {
394 if ( propertyNames[j].length() )
395 {
396 PropertyNodes::iterator propiter = propertyNodes.find( propertyNames[j] ) ;
397 if ( propiter != propertyNodes.end() )
398 {
399 wxXmlNode* prop = propiter->second ;
400 const wxPropertyInfo* pi = classInfo->FindPropertyInfo( propertyNames[j].c_str() ) ;
401 if ( pi->GetTypeInfo()->GetKind() == wxT_COLLECTION )
402 {
403 const wxCollectionTypeInfo* collType = dynamic_cast< const wxCollectionTypeInfo* >( pi->GetTypeInfo() ) ;
404 const wxTypeInfo * elementType = collType->GetElementType() ;
405 while( prop )
406 {
407 if ( prop->GetName() != wxT("element") )
408 {
409 wxLogError( _("A non empty collection must consist of 'element' nodes" ) ) ;
410 break ;
411 }
412 wxXmlNode* elementContent = prop->GetChildren() ;
413 if ( elementContent )
414 {
415 // we skip empty elements
416 if ( elementType->IsObjectType() )
417 {
418 int valueId = ReadComponent( elementContent , callbacks ) ;
419 if ( valueId != wxInvalidObjectID )
420 {
421 if ( pi->GetAccessor()->HasAdder() )
422 callbacks->AddToPropertyCollectionAsObject( objectID , classInfo , pi , valueId ) ;
423 // TODO for collections we must have a notation on taking over ownership or not
424 if ( elementType->GetKind() == wxT_OBJECT && valueId != wxNullObjectID )
425 callbacks->DestroyObject( valueId , GetObjectClassInfo( valueId ) ) ;
426 }
427 }
428 else
429 {
430 wxxVariant elementValue = ReadValue( elementContent , elementType ) ;
431 if ( pi->GetAccessor()->HasAdder() )
432 callbacks->AddToPropertyCollection( objectID , classInfo ,pi , elementValue ) ;
433 }
434 }
435 prop = prop->GetNext() ;
436 }
437 }
438 else if ( pi->GetTypeInfo()->IsObjectType() )
439 {
440 // and object can either be streamed out a string or as an object
441 // in case we have no node, then the object's streaming out has been vetoed
442 if ( prop )
443 {
444 if ( prop->GetName() == wxT("object") )
445 {
446 int valueId = ReadComponent( prop , callbacks ) ;
447 if ( valueId != wxInvalidObjectID )
448 {
449 callbacks->SetPropertyAsObject( objectID , classInfo , pi , valueId ) ;
450 if ( pi->GetTypeInfo()->GetKind() == wxT_OBJECT && valueId != wxNullObjectID )
451 callbacks->DestroyObject( valueId , GetObjectClassInfo( valueId ) ) ;
452 }
453 }
454 else
455 {
456 wxASSERT( pi->GetTypeInfo()->HasStringConverters() ) ;
457 wxxVariant nodeval = ReadValue( prop , pi->GetTypeInfo() ) ;
458 callbacks->SetProperty( objectID, classInfo ,pi , nodeval ) ;
459 }
460 }
461 }
462 else if ( pi->GetTypeInfo()->IsDelegateType() )
463 {
464 if ( prop )
465 {
466 wxString resstring = prop->GetContent() ;
467 wxInt32 pos = resstring.Find('.') ;
468 if ( pos != wxNOT_FOUND )
469 {
470 int sinkOid = atol(resstring.Left(pos).ToAscii()) ;
471 wxString handlerName = resstring.Mid(pos+1) ;
472 wxClassInfo* sinkClassInfo = GetObjectClassInfo( sinkOid ) ;
473
474 callbacks->SetConnect( objectID , classInfo , pi , sinkClassInfo ,
475 sinkClassInfo->FindHandlerInfo(handlerName) , sinkOid ) ;
476 }
477 else
478 {
479 wxLogError( _("incorrect event handler string, missing dot") ) ;
480 }
481 }
482
483 }
484 else
485 {
486 wxxVariant nodeval = ReadValue( prop , pi->GetTypeInfo() ) ;
487 if( pi->GetFlags() & wxPROP_ENUM_STORE_LONG )
488 {
489 const wxEnumTypeInfo *eti = dynamic_cast<const wxEnumTypeInfo*>( pi->GetTypeInfo() ) ;
490 if ( eti )
491 {
492 long realval ;
493 eti->ConvertToLong( nodeval , realval ) ;
494 nodeval = wxxVariant( realval ) ;
495 }
496 else
497 {
498 wxLogError( _("Type must have enum - long conversion") ) ;
499 }
500 }
501 callbacks->SetProperty( objectID, classInfo ,pi , nodeval ) ;
502 }
503 }
504 }
505 }
506
507 delete[] createParams ;
508 delete[] createParamOids ;
509 delete[] createClassInfos ;
510
511 return objectID;
512 }
513
514 wxxVariant wxXmlReader::ReadValue(wxXmlNode *node,
515 const wxTypeInfo *type )
516 {
517 wxString content ;
518 if ( node )
519 content = node->GetContent() ;
520 wxxVariant result ;
521 type->ConvertFromString( content , result ) ;
522 return result ;
523 }
524
525 int wxXmlReader::ReadObject( const wxString &name , wxDepersister *callbacks)
526 {
527 wxXmlNode *iter = m_parent->GetChildren() ;
528 while ( iter )
529 {
530 wxString entryName ;
531 if ( iter->GetPropVal(wxT("name"), &entryName) )
532 {
533 if ( entryName == name )
534 return ReadComponent( iter->GetChildren() , callbacks ) ;
535 }
536 iter = iter->GetNext() ;
537 }
538 return wxInvalidObjectID ;
539 }
540
541 #endif