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 
wxXmlAddContentToNode(wxXmlNode * node,const wxString & data)53 void wxXmlAddContentToNode( wxXmlNode* node, const wxString& data )
54 {
55     node->AddChild(new wxXmlNode(wxXML_TEXT_NODE, wxT("value"), data ) );
56 }
57 
wxXmlGetContentFromNode(wxXmlNode * node)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 
PushwxObjectXmlWriter::wxObjectXmlWriterInternal72     void Push( wxXmlNode *newCurrent )
73     {
74         m_objectStack.push_back( m_current );
75         m_current = newCurrent;
76     }
77 
PopwxObjectXmlWriter::wxObjectXmlWriterInternal78     void Pop()
79     {
80         m_current = m_objectStack.back();
81         m_objectStack.pop_back();
82     }
83 };
84 
wxObjectXmlWriter(wxXmlNode * rootnode)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 
~wxObjectXmlWriter()92 wxObjectXmlWriter::~wxObjectXmlWriter()
93 {
94     delete m_data;
95 }
96 
DoBeginWriteTopLevelEntry(const wxString & name)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 
DoEndWriteTopLevelEntry(const wxString & WXUNUSED (name))106 void wxObjectXmlWriter::DoEndWriteTopLevelEntry( const wxString &WXUNUSED(name) )
107 {
108     m_data->Pop();
109 }
110 
DoBeginWriteObject(const wxObject * WXUNUSED (object),const wxClassInfo * classInfo,int objectID,const wxStringToAnyHashMap & metadata)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 
DoEndWriteObject(const wxObject * WXUNUSED (object),const wxClassInfo * WXUNUSED (classInfo),int WXUNUSED (objectID))130 void wxObjectXmlWriter::DoEndWriteObject(const wxObject *WXUNUSED(object),
131                                    const wxClassInfo *WXUNUSED(classInfo),
132                                    int WXUNUSED(objectID) )
133 {
134     m_data->Pop();
135 }
136 
DoWriteSimpleType(const wxAny & value)137 void wxObjectXmlWriter::DoWriteSimpleType( const wxAny &value )
138 {
139     wxXmlAddContentToNode( m_data->m_current,wxAnyGetAsString(value) );
140 }
141 
DoBeginWriteElement()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 
DoEndWriteElement()150 void wxObjectXmlWriter::DoEndWriteElement()
151 {
152     m_data->Pop();
153 }
154 
DoBeginWriteProperty(const wxPropertyInfo * pi)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 
DoEndWriteProperty(const wxPropertyInfo * WXUNUSED (propInfo))164 void wxObjectXmlWriter::DoEndWriteProperty(const wxPropertyInfo *WXUNUSED(propInfo) )
165 {
166     m_data->Pop();
167 }
168 
DoWriteRepeatedObject(int objectID)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 
DoWriteNullObject()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 
DoWriteDelegate(const wxObject * WXUNUSED (object),const wxClassInfo * WXUNUSED (classInfo),const wxPropertyInfo * WXUNUSED (pi),const wxObject * eventSink,int sinkObjectID,const wxClassInfo * WXUNUSED (eventSinkClassInfo),const wxHandlerInfo * handlerInfo)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 
ReadComponent(wxXmlNode * node,wxObjectReaderCallback * callbacks)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 
ReadValue(wxXmlNode * node,const wxTypeInfo * type)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 
ReadObject(const wxString & name,wxObjectReaderCallback * callbacks)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
553