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