Document domain parameter of wxTranslations::GetTranslatedString().
[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 // Copyright: (c) 2003 Stefan Csomor
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_EXTENDED_RTTI
19
20 #include "wx/xtistrm.h"
21
22 #ifndef WX_PRECOMP
23 #include "wx/object.h"
24 #include "wx/hash.h"
25 #include "wx/event.h"
26 #endif
27
28 #include "wx/xml/xml.h"
29 #include "wx/tokenzr.h"
30 #include "wx/txtstrm.h"
31 #include "wx/xtistrm.h"
32 #include "wx/xtixml.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, const wxStringToAnyHashMap &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 wxStringToAnyHashMap::const_iterator it, en;
121 for( it = metadata.begin(), en = metadata.end(); it != en; ++it )
122 {
123 pnode->AddProperty( it->first, wxAnyGetAsString(it->second) );
124 }
125
126 m_data->m_current->AddChild(pnode);
127 m_data->Push( pnode );
128 }
129
130 void wxObjectXmlWriter::DoEndWriteObject(const wxObject *WXUNUSED(object),
131 const wxClassInfo *WXUNUSED(classInfo),
132 int WXUNUSED(objectID) )
133 {
134 m_data->Pop();
135 }
136
137 void wxObjectXmlWriter::DoWriteSimpleType( const wxAny &value )
138 {
139 wxXmlAddContentToNode( m_data->m_current,wxAnyGetAsString(value) );
140 }
141
142 void wxObjectXmlWriter::DoBeginWriteElement()
143 {
144 wxXmlNode *pnode;
145 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("element") );
146 m_data->m_current->AddChild(pnode);
147 m_data->Push( pnode );
148 }
149
150 void wxObjectXmlWriter::DoEndWriteElement()
151 {
152 m_data->Pop();
153 }
154
155 void wxObjectXmlWriter::DoBeginWriteProperty(const wxPropertyInfo *pi )
156 {
157 wxXmlNode *pnode;
158 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("prop") );
159 pnode->AddProperty(wxT("name"), pi->GetName() );
160 m_data->m_current->AddChild(pnode);
161 m_data->Push( pnode );
162 }
163
164 void wxObjectXmlWriter::DoEndWriteProperty(const wxPropertyInfo *WXUNUSED(propInfo) )
165 {
166 m_data->Pop();
167 }
168
169 void wxObjectXmlWriter::DoWriteRepeatedObject( int objectID )
170 {
171 wxXmlNode *pnode;
172 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("object"));
173 pnode->AddProperty(wxString(wxT("href")), wxString::Format( wxT("%d"), objectID ) );
174 m_data->m_current->AddChild(pnode);
175 }
176
177 void wxObjectXmlWriter::DoWriteNullObject()
178 {
179 wxXmlNode *pnode;
180 pnode = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("object"));
181 m_data->m_current->AddChild(pnode);
182 }
183
184 void wxObjectXmlWriter::DoWriteDelegate( const wxObject *WXUNUSED(object),
185 const wxClassInfo* WXUNUSED(classInfo),
186 const wxPropertyInfo *WXUNUSED(pi),
187 const wxObject *eventSink, int sinkObjectID,
188 const wxClassInfo* WXUNUSED(eventSinkClassInfo),
189 const wxHandlerInfo* handlerInfo )
190 {
191 if ( eventSink != NULL && handlerInfo != NULL )
192 {
193 wxXmlAddContentToNode( m_data->m_current,
194 wxString::Format(wxT("%d.%s"), sinkObjectID, handlerInfo->GetName().c_str()) );
195 }
196 }
197
198
199 // ----------------------------------------------------------------------------
200 // wxObjectXmlReader
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 wxObjectXmlReader::ReadComponent(wxXmlNode *node, wxObjectReaderCallback *callbacks)
210 {
211 wxASSERT_MSG( callbacks, wxT("Does not support reading without a Depersistor") );
212 wxString className;
213 wxClassInfo *classInfo;
214
215 wxAny *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->GetAttribute(wxT("href"), &ObjectIdString ) )
227 {
228 objectID = atoi( ObjectIdString.ToAscii() );
229 if ( HasObjectClassInfo( objectID ) )
230 {
231 return objectID;
232 }
233 else
234 {
235 wxLogError( _("Forward hrefs are not supported") );
236 return wxInvalidObjectID;
237 }
238 }
239 if ( !node->GetAttribute(wxT("id"), &ObjectIdString ) )
240 {
241 return wxNullObjectID;
242 }
243 }
244 if (!node->GetAttribute(wxT("class"), &className))
245 {
246 // No class name. Eek. FIXME: error handling
247 return wxInvalidObjectID;
248 }
249
250 classInfo = wxClassInfo::FindClass(className);
251 if ( classInfo == NULL )
252 {
253 wxLogError( wxString::Format(_("unknown class %s"),className ) );
254 return wxInvalidObjectID;
255 }
256
257 if ( children != NULL && children->GetType() == wxXML_TEXT_NODE )
258 {
259 wxLogError(_("objects cannot have XML Text Nodes") );
260 return wxInvalidObjectID;
261 }
262 if (!node->GetAttribute(wxT("id"), &ObjectIdString))
263 {
264 wxLogError(_("Objects must have an id attribute") );
265 // No object id. Eek. FIXME: error handling
266 return wxInvalidObjectID;
267 }
268 objectID = atoi( ObjectIdString.ToAscii() );
269
270 // is this object already has been streamed in, return it here
271 if ( HasObjectClassInfo( objectID ) )
272 {
273 wxLogError ( wxString::Format(_("Doubly used id : %d"), objectID ) );
274 return wxInvalidObjectID;
275 }
276
277 // new object, start with allocation
278 // first make the object know to our internal registry
279 SetObjectClassInfo( objectID, classInfo );
280
281 wxStringToAnyHashMap metadata;
282 wxXmlProperty *xp = node->GetAttributes();
283 while ( xp )
284 {
285 if ( xp->GetName() != wxString(wxT("class")) &&
286 xp->GetName() != wxString(wxT("id")) )
287 {
288 metadata[xp->GetName()] = wxAny( xp->GetValue() );
289 }
290 xp = xp->GetNext();
291 }
292 if ( !classInfo->NeedsDirectConstruction() )
293 callbacks->AllocateObject(objectID, classInfo, metadata);
294
295 //
296 // stream back the Create parameters first
297 createParams = new wxAny[ classInfo->GetCreateParamCount() ];
298 createParamOids = new int[classInfo->GetCreateParamCount() ];
299 createClassInfos = new const wxClassInfo*[classInfo->GetCreateParamCount() ];
300
301 #if wxUSE_UNICODE
302 typedef map<wstring, wxXmlNode *> PropertyNodes;
303 typedef vector<wstring> PropertyNames;
304 #else
305 typedef map<string, wxXmlNode *> PropertyNodes;
306 typedef vector<string> PropertyNames;
307 #endif
308 PropertyNodes propertyNodes;
309 PropertyNames propertyNames;
310
311 while( children )
312 {
313 wxString name;
314 children->GetAttribute( wxT("name"), &name );
315 propertyNames.push_back( (const wxChar*)name.c_str() );
316 propertyNodes[(const wxChar*)name.c_str()] = children->GetChildren();
317 children = children->GetNext();
318 }
319
320 for ( int i = 0; i <classInfo->GetCreateParamCount(); ++i )
321 {
322 const wxChar* paramName = classInfo->GetCreateParamName(i);
323 PropertyNodes::iterator propiter = propertyNodes.find( paramName );
324 const wxPropertyInfo* pi = classInfo->FindPropertyInfo( paramName );
325 if ( pi == 0 )
326 {
327 wxLogError( wxString::Format(_("Unknown Property %s"),paramName) );
328 }
329 // if we don't have the value of a create param set in the xml
330 // we use the default value
331 if ( propiter != propertyNodes.end() )
332 {
333 wxXmlNode* prop = propiter->second;
334 if ( pi->GetTypeInfo()->IsObjectType() )
335 {
336 createParamOids[i] = ReadComponent( prop, callbacks );
337 createClassInfos[i] =
338 wx_dynamic_cast(const wxClassTypeInfo*, pi->GetTypeInfo())->GetClassInfo();
339 }
340 else
341 {
342 createParamOids[i] = wxInvalidObjectID;
343 createParams[i] = ReadValue( prop, pi->GetTypeInfo() );
344 if( pi->GetFlags() & wxPROP_ENUM_STORE_LONG )
345 {
346 const wxEnumTypeInfo *eti =
347 wx_dynamic_cast(const wxEnumTypeInfo*, pi->GetTypeInfo() );
348 if ( eti )
349 {
350 long realval;
351 eti->ConvertToLong( createParams[i], realval );
352 createParams[i] = wxAny( realval );
353 }
354 else
355 {
356 wxLogError( _("Type must have enum - long conversion") );
357 }
358 }
359 createClassInfos[i] = NULL;
360 }
361
362 for ( size_t j = 0; j < propertyNames.size(); ++j )
363 {
364 if ( propertyNames[j] == paramName )
365 {
366 propertyNames[j] = wxEmptyString;
367 break;
368 }
369 }
370 }
371 else
372 {
373 if ( pi->GetTypeInfo()->IsObjectType() )
374 {
375 createParamOids[i] = wxNullObjectID;
376 createClassInfos[i] =
377 wx_dynamic_cast(const wxClassTypeInfo*, pi->GetTypeInfo())->GetClassInfo();
378 }
379 else
380 {
381 createParams[i] = pi->GetDefaultValue();
382 createParamOids[i] = wxInvalidObjectID;
383 }
384 }
385 }
386
387 // got the parameters. Call the Create method
388 if ( classInfo->NeedsDirectConstruction() )
389 callbacks->ConstructObject(objectID, classInfo,
390 classInfo->GetCreateParamCount(),
391 createParams, createParamOids, createClassInfos, metadata );
392 else
393 callbacks->CreateObject(objectID, classInfo,
394 classInfo->GetCreateParamCount(),
395 createParams, createParamOids, createClassInfos, metadata );
396
397 // now stream in the rest of the properties, in the sequence their
398 // properties were written in the xml
399 for ( size_t j = 0; j < propertyNames.size(); ++j )
400 {
401 if ( !propertyNames[j].empty() )
402 {
403 PropertyNodes::iterator propiter = propertyNodes.find( propertyNames[j] );
404 if ( propiter != propertyNodes.end() )
405 {
406 wxXmlNode* prop = propiter->second;
407 const wxPropertyInfo* pi =
408 classInfo->FindPropertyInfo( propertyNames[j].c_str() );
409 if ( pi->GetTypeInfo()->GetKind() == wxT_COLLECTION )
410 {
411 const wxCollectionTypeInfo* collType =
412 wx_dynamic_cast( const wxCollectionTypeInfo*, pi->GetTypeInfo() );
413 const wxTypeInfo * elementType = collType->GetElementType();
414 while( prop )
415 {
416 if ( prop->GetName() != wxT("element") )
417 {
418 wxLogError( _("A non empty collection must consist of 'element' nodes" ) );
419 break;
420 }
421
422 wxXmlNode* elementContent = prop->GetChildren();
423 if ( elementContent )
424 {
425 // we skip empty elements
426 if ( elementType->IsObjectType() )
427 {
428 int valueId = ReadComponent( elementContent, callbacks );
429 if ( valueId != wxInvalidObjectID )
430 {
431 if ( pi->GetAccessor()->HasAdder() )
432 callbacks->AddToPropertyCollectionAsObject( objectID, classInfo, pi, valueId );
433 // TODO for collections we must have a notation on taking over ownership or not
434 if ( elementType->GetKind() == wxT_OBJECT && valueId != wxNullObjectID )
435 callbacks->DestroyObject( valueId, GetObjectClassInfo( valueId ) );
436 }
437 }
438 else
439 {
440 wxAny elementValue = ReadValue( elementContent, elementType );
441 if ( pi->GetAccessor()->HasAdder() )
442 callbacks->AddToPropertyCollection( objectID, classInfo,pi, elementValue );
443 }
444 }
445 prop = prop->GetNext();
446 }
447 }
448 else if ( pi->GetTypeInfo()->IsObjectType() )
449 {
450 // and object can either be streamed out a string or as an object
451 // in case we have no node, then the object's streaming out has been vetoed
452 if ( prop )
453 {
454 if ( prop->GetName() == wxT("object") )
455 {
456 int valueId = ReadComponent( prop, callbacks );
457 if ( valueId != wxInvalidObjectID )
458 {
459 callbacks->SetPropertyAsObject( objectID, classInfo, pi, valueId );
460 if ( pi->GetTypeInfo()->GetKind() == wxT_OBJECT && valueId != wxNullObjectID )
461 callbacks->DestroyObject( valueId, GetObjectClassInfo( valueId ) );
462 }
463 }
464 else
465 {
466 wxASSERT( pi->GetTypeInfo()->HasStringConverters() );
467 wxAny nodeval = ReadValue( prop, pi->GetTypeInfo() );
468 callbacks->SetProperty( objectID, classInfo,pi, nodeval );
469 }
470 }
471 }
472 else if ( pi->GetTypeInfo()->IsDelegateType() )
473 {
474 if ( prop )
475 {
476 wxString resstring = prop->GetContent();
477 wxInt32 pos = resstring.Find('.');
478 if ( pos != wxNOT_FOUND )
479 {
480 int sinkOid = atol(resstring.Left(pos).ToAscii());
481 wxString handlerName = resstring.Mid(pos+1);
482 wxClassInfo* sinkClassInfo = GetObjectClassInfo( sinkOid );
483
484 callbacks->SetConnect( objectID, classInfo, pi, sinkClassInfo,
485 sinkClassInfo->FindHandlerInfo(handlerName.c_str()), sinkOid );
486 }
487 else
488 {
489 wxLogError( _("incorrect event handler string, missing dot") );
490 }
491 }
492
493 }
494 else
495 {
496 wxAny nodeval = ReadValue( prop, pi->GetTypeInfo() );
497 if( pi->GetFlags() & wxPROP_ENUM_STORE_LONG )
498 {
499 const wxEnumTypeInfo *eti =
500 wx_dynamic_cast(const wxEnumTypeInfo*, pi->GetTypeInfo() );
501 if ( eti )
502 {
503 long realval;
504 eti->ConvertToLong( nodeval, realval );
505 nodeval = wxAny( realval );
506 }
507 else
508 {
509 wxLogError( _("Type must have enum - long conversion") );
510 }
511 }
512 callbacks->SetProperty( objectID, classInfo,pi, nodeval );
513 }
514 }
515 }
516 }
517
518 delete[] createParams;
519 delete[] createParamOids;
520 delete[] createClassInfos;
521
522 return objectID;
523 }
524
525 wxAny wxObjectXmlReader::ReadValue(wxXmlNode *node,
526 const wxTypeInfo *type )
527 {
528 wxString content;
529 if ( node )
530 content = node->GetContent();
531 wxAny result;
532 type->ConvertFromString( content, result );
533 return result;
534 }
535
536 int wxObjectXmlReader::ReadObject( const wxString &name, wxObjectReaderCallback *callbacks)
537 {
538 wxXmlNode *iter = m_parent->GetChildren();
539 while ( iter )
540 {
541 wxString entryName;
542 if ( iter->GetAttribute(wxT("name"), &entryName) )
543 {
544 if ( entryName == name )
545 return ReadComponent( iter->GetChildren(), callbacks );
546 }
547 iter = iter->GetNext();
548 }
549 return wxInvalidObjectID;
550 }
551
552 #endif // wxUSE_EXTENDED_RTTI