]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/xtixml.cpp
Minor source cleaning.
[wxWidgets.git] / src / common / xtixml.cpp
... / ...
CommitLineData
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// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16#pragma hdrstop
17#endif
18
19#ifndef WX_PRECOMP
20#include "wx/hash.h"
21#include "wx/object.h"
22#endif
23
24#include "wx/xml/xml.h"
25#include "wx/tokenzr.h"
26#include "wx/txtstrm.h"
27#include "wx/event.h"
28
29#if wxUSE_EXTENDED_RTTI
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
40using namespace std ;
41
42//
43// XML Streaming
44//
45
46// convenience functions
47
48void wxXmlAddContentToNode( wxXmlNode* node , const wxString& data )
49{
50 node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, wxT("value"), data ) );
51}
52
53wxString wxXmlGetContentFromNode( wxXmlNode *node )
54{
55 if ( node->GetChildren() )
56 return node->GetChildren()->GetContent() ;
57 else
58 return wxEmptyString ;
59}
60
61struct 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
80wxXmlWriter::wxXmlWriter( wxXmlNode * rootnode )
81{
82 m_data = new wxXmlWriterInternal() ;
83 m_data->m_root = rootnode ;
84 m_data->m_current = rootnode ;
85}
86
87wxXmlWriter::~wxXmlWriter()
88{
89 delete m_data ;
90}
91
92void 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
101void wxXmlWriter::DoEndWriteTopLevelEntry( const wxString &WXUNUSED(name) )
102{
103 m_data->Pop() ;
104}
105
106void 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
122void 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
128void wxXmlWriter::DoWriteSimpleType( wxxVariant &value )
129{
130 wxXmlAddContentToNode( m_data->m_current ,value.GetAsString() ) ;
131}
132
133void 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
141void wxXmlWriter::DoEndWriteElement()
142{
143 m_data->Pop() ;
144}
145
146void 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
155void 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
163void 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
172void 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
180void 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/*
200Reading components has not to be extended for components
201as properties are always sought by typeinfo over all levels
202and create params are always toplevel class only
203*/
204
205int 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
510wxxVariant 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
521int 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