]> git.saurik.com Git - wxWidgets.git/blob - src/common/xtistrm.cpp
fixed version number expansion
[wxWidgets.git] / src / common / xtistrm.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 #ifdef __GNUG__
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/xtistrm.h"
31 #include "wx/txtstrm.h"
32
33 #if wxUSE_EXTENDED_RTTI
34 #include <map>
35 #include <vector>
36 #include <string>
37
38 using namespace std ;
39
40 // ----------------------------------------------------------------------------
41 // streaming xml out
42 // ----------------------------------------------------------------------------
43
44 void WriteComponent(wxObject *Object, const wxClassInfo *ClassInfo, wxXmlNode *parent, const wxString& nodeName , int &nextId , map< wxObject* , int > &writtenObjects ) ;
45
46 void WriteComponentProperties( wxObject* obj , const wxClassInfo* ci , wxXmlNode *onode , int &nextId, map< wxObject* , int > &writtenObjects, map< string , int > &writtenProperties)
47 {
48 const wxPropertyInfo *pi = ci->GetFirstProperty() ;
49 while( pi )
50 {
51 if ( writtenProperties.find( pi->GetName() ) == writtenProperties.end() )
52 {
53 writtenProperties[ pi->GetName() ] = 1 ;
54 const wxClassTypeInfo* cti = dynamic_cast< const wxClassTypeInfo* > ( pi->GetTypeInfo() ) ;
55 if ( cti )
56 {
57 const wxClassInfo* pci = cti->GetClassInfo() ;
58 WriteComponent( pci->VariantToInstance( pi->GetAccessor()->GetProperty(obj) ) , pci , onode , pi->GetName() , nextId , writtenObjects ) ;
59 }
60 else
61 {
62 const wxDelegateTypeInfo* dti = dynamic_cast< const wxDelegateTypeInfo* > ( pi->GetTypeInfo() ) ;
63 if ( dti )
64 {
65 // in which form should we stream out these ?
66 }
67 else
68 {
69 wxXmlNode *pnode;
70 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, pi->GetName() );
71 pi->GetAccessor()->WriteValue(pnode, obj ) ;
72 onode->AddChild(pnode);
73 }
74 }
75 }
76 pi = pi->GetNext() ;
77 }
78 const wxClassInfo** parents = ci->GetParents() ;
79 for ( int i = 0 ; parents[i] ; ++ i )
80 {
81 WriteComponentProperties( obj , parents[i] , onode , nextId , writtenObjects , writtenProperties ) ;
82 }
83 }
84
85 /*
86 Writing Components does have to take inheritance into account, that's why we are iterating
87 over our parents as well
88 */
89
90 void WriteComponent(wxObject *obj, const wxClassInfo *classInfo, wxXmlNode *parent, const wxString &nodeName)
91 {
92 int nextid = 0 ; // 0 is the root element
93 map< wxObject* , int > writtenobjects ;
94 WriteComponent( obj , classInfo, parent, nodeName , nextid , writtenobjects ) ;
95 }
96
97 void WriteComponent(wxObject *obj, const wxClassInfo *classInfo, wxXmlNode *parent, const wxString& nodeName , int &nextId, map< wxObject* , int > &writtenObjects )
98 {
99 map< string , int > writtenProperties ;
100 wxXmlNode *onode;
101
102 onode = new wxXmlNode(wxXML_ELEMENT_NODE, nodeName);
103
104 onode->AddProperty(wxString("class"), wxString(classInfo->GetClassName()));
105 if ( obj == NULL )
106 {
107 wxXmlNode* nullnode = new wxXmlNode(wxXML_TEXT_NODE, wxEmptyString, "null");
108 onode->AddChild(nullnode);
109 }
110 else
111 {
112 // if we have already written this object, just insert an id
113 if ( writtenObjects.find( obj ) != writtenObjects.end() )
114 {
115 onode->AddProperty(wxString("id"), wxString::Format( "%d" , writtenObjects[obj] ) );
116 }
117 else
118 {
119 int id = nextId++ ;
120 writtenObjects[obj] = id ;
121 onode->AddProperty(wxString("id"), wxString::Format( "%d" , id ) );
122 WriteComponentProperties( obj , classInfo , onode , nextId , writtenObjects, writtenProperties) ;
123 }
124 }
125
126 parent->AddChild(onode);
127 }
128
129 // ----------------------------------------------------------------------------
130 // reading xml in
131 // ----------------------------------------------------------------------------
132
133 wxxVariant wxReader::ReadPropertyValueNoAssign(wxXmlNode *Node,
134 wxClassInfo *ClassInfo,
135 const wxPropertyInfo * &pi ,
136 wxIDepersist *Callbacks)
137 {
138 wxxVariant res;
139 int ChildID;
140
141 // form is:
142 // <propname type=foo>value</propname>
143
144 //ISSUE: NULL component references are streamed out as "null" text
145 // node. This is not in keeping with the XML mindset.
146
147 pi = ClassInfo->FindPropertyInfo(Node->GetName());
148 if (!pi)
149 {
150 // error handling, please
151 assert(!"Property not found in extended class info");
152 }
153
154 const wxClassTypeInfo* cti = dynamic_cast< const wxClassTypeInfo* > ( pi->GetTypeInfo() ) ;
155 if ( cti )
156 {
157 const wxClassInfo* eci = cti->GetClassInfo() ;
158
159 ChildID = ReadComponent(Node , Callbacks);
160 if (ChildID != -1)
161 {
162 if (genCode)
163 res = wxxVariant(GetObjectName(ChildID));
164 else
165 res = eci->InstanceToVariant(GetObject(ChildID));
166 }
167 else
168 {
169 if (genCode)
170 res = wxxVariant(wxString("NULL"));
171 else
172 res = eci->InstanceToVariant(NULL);
173 }
174 }
175 else
176 {
177 const wxDelegateTypeInfo* dti = dynamic_cast< const wxDelegateTypeInfo* > ( pi->GetTypeInfo() ) ;
178 if ( dti )
179 {
180 if (genCode)
181 {
182 // in which form should we code these ?
183 res = wxxVariant( wxXmlGetContentFromNode(Node) ) ;
184 }
185 else
186 {
187 res = wxxVariant( wxXmlGetContentFromNode(Node) ) ;
188 }
189 }
190 else
191 {
192 if (genCode)
193 {
194 if ( pi->GetTypeInfo()->GetKind() == wxT_STRING )
195 res = wxxVariant( wxString::Format("wxString(\"%s\")",wxXmlGetContentFromNode(Node)));
196 else
197 res = wxxVariant( wxString::Format("%s(%s)",pi->GetTypeName(),wxXmlGetContentFromNode(Node) ) );
198 }
199 else
200 res = pi->GetAccessor()->ReadValue(Node) ;
201 }
202 }
203 return res ;
204 }
205
206 void wxReader::ReadPropertyValue(wxXmlNode *Node,
207 wxClassInfo *ClassInfo,
208 int ObjectID ,
209 wxIDepersist *Callbacks)
210 {
211 const wxPropertyInfo *pi;
212 wxxVariant res = ReadPropertyValueNoAssign( Node , ClassInfo, pi , Callbacks ) ;
213
214 const wxDelegateTypeInfo* dti = dynamic_cast< const wxDelegateTypeInfo* > ( pi->GetTypeInfo() ) ;
215
216 if ( dti )
217 {
218 wxString resstring = res.Get<wxString>() ;
219 wxInt32 pos = resstring.Find('.') ;
220 assert( pos != wxNOT_FOUND ) ;
221 int handlerOid = atol(resstring.Left(pos)) ;
222 wxString handlerName = resstring.Mid(pos+1) ;
223
224 if (Callbacks)
225 Callbacks->SetConnect( ObjectID , ClassInfo , dti->GetEventType() , handlerName , handlerOid ) ;
226 }
227 else
228 {
229 if (Callbacks)
230 Callbacks->SetProperty(ObjectID, ClassInfo, pi , res);
231 }
232 }
233
234 struct wxReader::wxReaderInternal
235 {
236 /*
237 Code streamer will be storing names here. Runtime object streamer
238 will be storing actual pointers to objects here. The two are never
239 mixed. So the Objects array either has data, or the ObjectNames
240 array has data. Never both. Keyed by ObjectID (int)
241 */
242 map<int,wxObject *> Objects;
243
244 map<int,string> ObjectNames;
245 // only used when generating code, since the names loose the type info
246 map<int,wxClassInfo*> ObjectClasses;
247 };
248
249 wxReader::wxReader(bool GenerateCode) : genCode(GenerateCode)
250 {
251 Data = new wxReaderInternal;
252 }
253
254 wxReader::~wxReader()
255 {
256 delete Data;
257 }
258
259 wxObject *wxReader::GetObject(int id)
260 {
261 assert( Data->Objects.find(id) != Data->Objects.end() );
262 return Data->Objects[id];
263 }
264
265 wxString wxReader::GetObjectName(int id)
266 {
267 assert( Data->ObjectNames.find(id) != Data->ObjectNames.end() );
268 return wxString(Data->ObjectNames[id].c_str());
269 }
270
271 wxClassInfo* wxReader::GetObjectClassInfo(int id)
272 {
273 assert( Data->ObjectClasses.find(id) != Data->ObjectClasses.end() );
274 return Data->ObjectClasses[id] ;
275 }
276
277 void wxReader::SetObject(int id, wxObject *Object)
278 {
279 assert( Data->Objects.find(id) == Data->Objects.end() ) ;
280 Data->Objects[id] = Object;
281 }
282
283 void wxReader::SetObjectName(int id, const wxString &Name, wxClassInfo *ClassInfo )
284 {
285 assert( Data->ObjectNames.find(id) == Data->ObjectNames.end() ) ;
286 Data->ObjectNames[id] = (const char *)Name;
287 Data->ObjectClasses[id] = ClassInfo ;
288 }
289
290 /*
291 Reading components has not to be extended for components
292 as properties are always sought by typeinfo over all levels
293 and create params are always toplevel class only
294 */
295
296 int wxReader::ReadComponent(wxXmlNode *Node, wxIDepersist *Callbacks)
297 {
298 wxString ClassName;
299 wxClassInfo *ClassInfo;
300
301 wxxVariant *CreateParams ;
302 wxXmlNode *Children;
303 int ObjectID;
304
305 Callbacks->NotifyReader(this);
306
307 Children = Node->GetChildren();
308 if (!Node->GetPropVal("class", &ClassName))
309 {
310 // No class name. Eek. FIXME: error handling
311 return -1;
312 }
313 ClassInfo = wxClassInfo::FindClass(ClassName);
314 if (Node->GetType() == wxXML_TEXT_NODE)
315 {
316 assert( wxXmlGetContentFromNode(Node) == "null" ) ;
317 // this must be a NULL component reference. We just bail out
318 return -1;
319 }
320
321 wxString ObjectIdString ;
322 if (!Node->GetPropVal("id", &ObjectIdString))
323 {
324 // No object id. Eek. FIXME: error handling
325 return -1;
326 }
327
328 ObjectID = atoi( ObjectIdString.c_str() ) ;
329 // is this object already has been streamed in, return it here
330 if ( genCode )
331 {
332 if ( Data->ObjectNames.find( ObjectID ) != Data->ObjectNames.end() )
333 return ObjectID ;
334 }
335 else
336 {
337 if ( Data->Objects.find( ObjectID ) != Data->Objects.end() )
338 return ObjectID ;
339 }
340
341 // new object, start with allocation
342 Callbacks->AllocateObject(ObjectID, ClassInfo);
343
344 //
345 // stream back the Create parameters first
346 CreateParams = new wxxVariant[ ClassInfo->GetCreateParamCount() ] ;
347
348 typedef map<string, wxXmlNode *> PropertyNodes ;
349 typedef vector<string> PropertyNames ;
350
351 PropertyNodes propertyNodes ;
352 PropertyNames propertyNames ;
353
354 while( Children )
355 {
356 propertyNames.push_back( Children->GetName().c_str() ) ;
357 propertyNodes[Children->GetName().c_str()] = Children ;
358 Children = Children->GetNext() ;
359 }
360
361 for ( int i = 0 ; i <ClassInfo->GetCreateParamCount() ; ++i )
362 {
363 const wxChar* paramName = ClassInfo->GetCreateParamName(i) ;
364 PropertyNodes::iterator propiter = propertyNodes.find( paramName ) ;
365 // if we don't have the value of a create param set in the xml
366 // we use the default value
367 if ( propiter != propertyNodes.end() )
368 {
369 wxXmlNode* prop = propiter->second ;
370 wxPropertyInfo* pi ;
371 CreateParams[i] = ReadPropertyValueNoAssign( prop , ClassInfo , pi , Callbacks ) ;
372 // CreateParams[i] = ClassInfo->FindPropertyInfo( ClassInfo->GetCreateParamName(i) ->GetAccessor()->ReadValue( prop ) ;
373 for ( size_t j = 0 ; j < propertyNames.size() ; ++j )
374 {
375 if ( propertyNames[j] == paramName )
376 {
377 propertyNames[j] = "" ;
378 break ;
379 }
380 }
381 }
382 else
383 {
384 CreateParams[i] = ClassInfo->FindPropertyInfo( paramName )->GetDefaultValue() ;
385 }
386 }
387
388 // got the parameters. Call the Create method
389 Callbacks->CreateObject(ObjectID,
390 ClassInfo,
391 ClassInfo->GetCreateParamCount(),
392 &CreateParams[0]);
393
394 // now stream in the rest of the properties, in the sequence their properties were written in the xml
395 for ( size_t j = 0 ; j < propertyNames.size() ; ++j )
396 {
397 if ( propertyNames[j].length() )
398 {
399 PropertyNodes::iterator propiter = propertyNodes.find( propertyNames[j] ) ;
400 if ( propiter != propertyNodes.end() )
401 {
402 wxXmlNode* prop = propiter->second ;
403 string name = propiter->first ;
404 ReadPropertyValue( prop , ClassInfo , ObjectID , Callbacks ) ;
405 }
406 }
407 }
408 /*
409 for( PropertyNodes::iterator propiter = propertyNodes.begin() ; propiter != propertyNodes.end() ; propiter++ )
410 {
411 wxXmlNode* prop = propiter->second ;
412 string name = propiter->first ;
413 ReadPropertyValue( prop , ClassInfo , ObjectID , Callbacks ) ;
414 }
415 */
416
417 // FIXME: if the list of children is not NULL now, then that means that
418 // there were properties in the XML not represented in the meta data
419 // this just needs error handling.
420 assert(!Children);
421
422 delete[] CreateParams ;
423
424 return ObjectID;
425 }
426
427 // ----------------------------------------------------------------------------
428 // depersisting to memory
429 // ----------------------------------------------------------------------------
430
431 void wxIDepersistRuntime::AllocateObject(int ObjectID, wxClassInfo *ClassInfo)
432 {
433 wxObject *O;
434 O = ClassInfo->CreateObject();
435 Reader->SetObject(ObjectID, O);
436 }
437
438 void wxIDepersistRuntime::CreateObject(int ObjectID,
439 wxClassInfo *ClassInfo,
440 int ParamCount,
441 wxxVariant *Params)
442 {
443 wxObject *O;
444 O = Reader->GetObject(ObjectID);
445 ClassInfo->Create(O, ParamCount, Params);
446 }
447
448 void wxIDepersistRuntime::SetProperty(int ObjectID,
449 wxClassInfo *WXUNUSED(ClassInfo),
450 const wxPropertyInfo* PropertyInfo,
451 const wxxVariant &Value)
452 {
453 wxObject *O;
454 O = Reader->GetObject(ObjectID);
455 PropertyInfo->GetAccessor()->SetProperty( O , Value ) ;
456 }
457
458 void wxIDepersistRuntime::SetConnect(int EventSourceObjectID,
459 wxClassInfo *WXUNUSED(EventSourceClassInfo),
460 int eventType ,
461 const wxString &handlerName ,
462 int EventSinkObjectID )
463 {
464 wxWindow *ehsource = dynamic_cast< wxWindow* >( Reader->GetObject( EventSourceObjectID ) ) ;
465 wxEvtHandler *ehsink = dynamic_cast< wxEvtHandler *>(Reader->GetObject(EventSinkObjectID) ) ;
466
467 if ( ehsource && ehsink )
468 {
469 ehsource->Connect( ehsource->GetId() , eventType ,
470 ehsink->GetClassInfo()->FindHandlerInfo(handlerName)->GetEventFunction() , NULL /*user data*/ ,
471 ehsink ) ;
472 }
473 }
474
475 // ----------------------------------------------------------------------------
476 // depersisting to code
477 // ----------------------------------------------------------------------------
478
479
480 void wxIDepersistCode::AllocateObject(int ObjectID, wxClassInfo *ClassInfo)
481 {
482 wxString objectName = wxString::Format( "LocalObject_%d" , ObjectID ) ;
483 fp->WriteString( wxString::Format( "\t%s *%s = new %s;\n",
484 ClassInfo->GetClassName(),
485 objectName,
486 ClassInfo->GetClassName()) );
487 Reader->SetObjectName(ObjectID, objectName, ClassInfo);
488 }
489
490 void wxIDepersistCode::CreateObject(int ObjectID,
491 wxClassInfo *WXUNUSED(ClassInfo),
492 int ParamCount,
493 wxxVariant *Params)
494 {
495 int i;
496 fp->WriteString( wxString::Format( "\t%s->Create(", Reader->GetObjectName(ObjectID) ) );
497 for (i = 0; i < ParamCount; i++)
498 {
499 fp->WriteString( wxString::Format( "%s", (const char *)Params[i].Get<wxString>() ) );
500 if (i < ParamCount - 1)
501 fp->WriteString( ", ");
502 }
503 fp->WriteString( ");\n");
504 }
505
506 void wxIDepersistCode::SetProperty(int ObjectID,
507 wxClassInfo *WXUNUSED(ClassInfo),
508 const wxPropertyInfo* PropertyInfo,
509 const wxxVariant &Value)
510 {
511 wxString d = Value.Get<wxString>() ;
512 fp->WriteString( wxString::Format( "\t%s->%s(%s);\n",
513 Reader->GetObjectName(ObjectID),
514 PropertyInfo->GetAccessor()->GetSetterName(),
515 d) );
516 }
517
518 void wxIDepersistCode::SetConnect(int EventSourceObjectID,
519 wxClassInfo *WXUNUSED(EventSourceClassInfo),
520 int eventType ,
521 const wxString &handlerName ,
522 int EventSinkObjectID )
523 {
524 wxString ehsource = Reader->GetObjectName( EventSourceObjectID ) ;
525 wxString ehsink = Reader->GetObjectName(EventSinkObjectID) ;
526 wxString ehsinkClass = Reader->GetObjectClassInfo(EventSinkObjectID)->GetClassName() ;
527
528 fp->WriteString( wxString::Format( "\t%s->Connect( %s->GetId() , %d , (wxObjectEventFunction)(wxEventFunction) & %s::%s , NULL , %s ) ;" ,
529 ehsource , ehsource , eventType , ehsinkClass , handlerName , ehsink ) );
530 }
531
532 #endif