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