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