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