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