]> git.saurik.com Git - wxWidgets.git/blob - src/common/xtixml.cpp
return the index of the inserted/appended item
[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 wxASSERT_MSG( HasObjectClassInfo( objectID ) , wxT("Forward hrefs are not supported") ) ;
230 return objectID ;
231 }
232 if ( !node->GetPropVal(wxT("id") , &ObjectIdString ) )
233 {
234 return wxNullObjectID;
235 }
236 }
237 if (!node->GetPropVal(wxT("class"), &className))
238 {
239 // No class name. Eek. FIXME: error handling
240 return wxInvalidObjectID;
241 }
242 classInfo = wxClassInfo::FindClass(className);
243 wxASSERT_MSG( classInfo , wxString::Format(wxT("unknown class %s"),className ) ) ;
244 wxASSERT_MSG( !children || children->GetType() != wxXML_TEXT_NODE , wxT("objects cannot have XML Text Nodes") ) ;
245 if (!node->GetPropVal(wxT("id"), &ObjectIdString))
246 {
247 wxASSERT_MSG(0,wxT("Objects must have an id attribute") ) ;
248 // No object id. Eek. FIXME: error handling
249 return wxInvalidObjectID;
250 }
251 objectID = atoi( ObjectIdString.ToAscii() ) ;
252 // is this object already has been streamed in, return it here
253 wxASSERT_MSG( !HasObjectClassInfo( objectID ) , wxString::Format(wxT("Doubly used id : %d"), objectID ) ) ;
254
255 // new object, start with allocation
256 // first make the object know to our internal registry
257 SetObjectClassInfo( objectID , classInfo ) ;
258
259 wxxVariantArray metadata ;
260 wxXmlProperty *xp = node->GetProperties() ;
261 while ( xp )
262 {
263 if ( xp->GetName() != wxString(wxT("class")) && xp->GetName() != wxString(wxT("id")) )
264 {
265 metadata.Add( new wxxVariant( xp->GetValue() , xp->GetName() ) ) ;
266 }
267 xp = xp->GetNext() ;
268 }
269 if ( !classInfo->NeedsDirectConstruction() )
270 callbacks->AllocateObject(objectID, classInfo, metadata);
271
272 //
273 // stream back the Create parameters first
274 createParams = new wxxVariant[ classInfo->GetCreateParamCount() ] ;
275 createParamOids = new int[classInfo->GetCreateParamCount() ] ;
276 createClassInfos = new const wxClassInfo*[classInfo->GetCreateParamCount() ] ;
277
278 #if wxUSE_UNICODE
279 typedef map<wstring, wxXmlNode *> PropertyNodes ;
280 typedef vector<wstring> PropertyNames ;
281 #else
282 typedef map<string, wxXmlNode *> PropertyNodes ;
283 typedef vector<string> PropertyNames ;
284 #endif
285 PropertyNodes propertyNodes ;
286 PropertyNames propertyNames ;
287
288 while( children )
289 {
290 wxString name ;
291 children->GetPropVal( wxT("name") , &name ) ;
292 propertyNames.push_back( name.c_str() ) ;
293 propertyNodes[name.c_str()] = children->GetChildren() ;
294 children = children->GetNext() ;
295 }
296
297 for ( int i = 0 ; i <classInfo->GetCreateParamCount() ; ++i )
298 {
299 const wxChar* paramName = classInfo->GetCreateParamName(i) ;
300 PropertyNodes::iterator propiter = propertyNodes.find( paramName ) ;
301 const wxPropertyInfo* pi = classInfo->FindPropertyInfo( paramName ) ;
302 wxASSERT_MSG(pi,wxString::Format(wxT("Unkown Property %s"),paramName) ) ;
303 // if we don't have the value of a create param set in the xml
304 // we use the default value
305 if ( propiter != propertyNodes.end() )
306 {
307 wxXmlNode* prop = propiter->second ;
308 if ( pi->GetTypeInfo()->IsObjectType() )
309 {
310 createParamOids[i] = ReadComponent( prop , callbacks ) ;
311 createClassInfos[i] = dynamic_cast<const wxClassTypeInfo*>(pi->GetTypeInfo())->GetClassInfo() ;
312 }
313 else
314 {
315 createParamOids[i] = wxInvalidObjectID ;
316 createParams[i] = ReadValue( prop , pi->GetTypeInfo() ) ;
317 if( pi->GetFlags() & wxPROP_ENUM_STORE_LONG )
318 {
319 const wxEnumTypeInfo *eti = dynamic_cast<const wxEnumTypeInfo*>( pi->GetTypeInfo() ) ;
320 wxASSERT_MSG( eti , wxT("Type must have enum - long conversion") ) ;
321
322 long realval ;
323 eti->ConvertToLong( createParams[i] , realval ) ;
324 createParams[i] = wxxVariant( realval ) ;
325 }
326 createClassInfos[i] = NULL ;
327 }
328
329 for ( size_t j = 0 ; j < propertyNames.size() ; ++j )
330 {
331 if ( propertyNames[j] == paramName )
332 {
333 propertyNames[j] = wxEmptyString ;
334 break ;
335 }
336 }
337 }
338 else
339 {
340 if ( pi->GetTypeInfo()->IsObjectType() )
341 {
342 createParamOids[i] = wxNullObjectID ;
343 createClassInfos[i] = dynamic_cast<const wxClassTypeInfo*>(pi->GetTypeInfo())->GetClassInfo() ;
344 }
345 else
346 {
347 createParams[i] = pi->GetDefaultValue() ;
348 createParamOids[i] = wxInvalidObjectID ;
349 }
350 }
351 }
352
353 // got the parameters. Call the Create method
354 if ( classInfo->NeedsDirectConstruction() )
355 callbacks->ConstructObject(objectID, classInfo,
356 classInfo->GetCreateParamCount(),
357 createParams, createParamOids, createClassInfos, metadata );
358 else
359 callbacks->CreateObject(objectID, classInfo,
360 classInfo->GetCreateParamCount(),
361 createParams, createParamOids, createClassInfos, metadata );
362
363 // now stream in the rest of the properties, in the sequence their properties were written in the xml
364 for ( size_t j = 0 ; j < propertyNames.size() ; ++j )
365 {
366 if ( propertyNames[j].length() )
367 {
368 PropertyNodes::iterator propiter = propertyNodes.find( propertyNames[j] ) ;
369 if ( propiter != propertyNodes.end() )
370 {
371 wxXmlNode* prop = propiter->second ;
372 const wxPropertyInfo* pi = classInfo->FindPropertyInfo( propertyNames[j].c_str() ) ;
373 if ( pi->GetTypeInfo()->GetKind() == wxT_COLLECTION )
374 {
375 const wxCollectionTypeInfo* collType = dynamic_cast< const wxCollectionTypeInfo* >( pi->GetTypeInfo() ) ;
376 const wxTypeInfo * elementType = collType->GetElementType() ;
377 while( prop )
378 {
379 wxASSERT_MSG(prop->GetName() == wxT("element") , wxT("A non empty collection must consist of 'element' nodes")) ;
380 wxXmlNode* elementContent = prop->GetChildren() ;
381 if ( elementContent )
382 {
383 // we skip empty elements
384 if ( elementType->IsObjectType() )
385 {
386 int valueId = ReadComponent( elementContent , callbacks ) ;
387 if ( valueId != wxInvalidObjectID )
388 {
389 if ( pi->GetAccessor()->HasAdder() )
390 callbacks->AddToPropertyCollectionAsObject( objectID , classInfo , pi , valueId ) ;
391 // TODO for collections we must have a notation on taking over ownership or not
392 if ( elementType->GetKind() == wxT_OBJECT && valueId != wxNullObjectID )
393 callbacks->DestroyObject( valueId , GetObjectClassInfo( valueId ) ) ;
394 }
395 }
396 else
397 {
398 wxxVariant elementValue = ReadValue( elementContent , elementType ) ;
399 if ( pi->GetAccessor()->HasAdder() )
400 callbacks->AddToPropertyCollection( objectID , classInfo ,pi , elementValue ) ;
401 }
402 }
403 prop = prop->GetNext() ;
404 }
405 }
406 else if ( pi->GetTypeInfo()->IsObjectType() )
407 {
408 // and object can either be streamed out a string or as an object
409 // in case we have no node, then the object's streaming out has been vetoed
410 if ( prop )
411 {
412 if ( prop->GetName() == wxT("object") )
413 {
414 int valueId = ReadComponent( prop , callbacks ) ;
415 if ( valueId != wxInvalidObjectID )
416 {
417 callbacks->SetPropertyAsObject( objectID , classInfo , pi , valueId ) ;
418 if ( pi->GetTypeInfo()->GetKind() == wxT_OBJECT && valueId != wxNullObjectID )
419 callbacks->DestroyObject( valueId , GetObjectClassInfo( valueId ) ) ;
420 }
421 }
422 else
423 {
424 wxASSERT( pi->GetTypeInfo()->HasStringConverters() ) ;
425 wxxVariant nodeval = ReadValue( prop , pi->GetTypeInfo() ) ;
426 callbacks->SetProperty( objectID, classInfo ,pi , nodeval ) ;
427 }
428 }
429 }
430 else if ( pi->GetTypeInfo()->IsDelegateType() )
431 {
432 if ( prop )
433 {
434 wxString resstring = prop->GetContent() ;
435 wxInt32 pos = resstring.Find('.') ;
436 assert( pos != wxNOT_FOUND ) ;
437 int sinkOid = atol(resstring.Left(pos).ToAscii()) ;
438 wxString handlerName = resstring.Mid(pos+1) ;
439 wxClassInfo* sinkClassInfo = GetObjectClassInfo( sinkOid ) ;
440
441 callbacks->SetConnect( objectID , classInfo , dynamic_cast<const wxDelegateTypeInfo*>(pi->GetTypeInfo()) , sinkClassInfo ,
442 sinkClassInfo->FindHandlerInfo(handlerName) , sinkOid ) ;
443 }
444
445 }
446 else
447 {
448 wxxVariant nodeval = ReadValue( prop , pi->GetTypeInfo() ) ;
449 if( pi->GetFlags() & wxPROP_ENUM_STORE_LONG )
450 {
451 const wxEnumTypeInfo *eti = dynamic_cast<const wxEnumTypeInfo*>( pi->GetTypeInfo() ) ;
452 wxASSERT_MSG( eti , wxT("Type must have enum - long conversion") ) ;
453
454 long realval ;
455 eti->ConvertToLong( nodeval , realval ) ;
456 nodeval = wxxVariant( realval ) ;
457 }
458 callbacks->SetProperty( objectID, classInfo ,pi , nodeval ) ;
459 }
460 }
461 }
462 }
463
464 delete[] createParams ;
465 delete[] createParamOids ;
466 delete[] createClassInfos ;
467
468 return objectID;
469 }
470
471 wxxVariant wxXmlReader::ReadValue(wxXmlNode *node,
472 const wxTypeInfo *type )
473 {
474 wxString content ;
475 if ( node )
476 content = node->GetContent() ;
477 wxxVariant result ;
478 type->ConvertFromString( content , result ) ;
479 return result ;
480 }
481
482 int wxXmlReader::ReadObject( const wxString &name , wxDepersister *callbacks)
483 {
484 wxXmlNode *iter = m_parent->GetChildren() ;
485 while ( iter )
486 {
487 wxString entryName ;
488 if ( iter->GetPropVal(wxT("name"), &entryName) )
489 {
490 if ( entryName == name )
491 return ReadComponent( iter->GetChildren() , callbacks ) ;
492 }
493 iter = iter->GetNext() ;
494 }
495 return wxInvalidObjectID ;
496 }
497
498 #endif