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