1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/xtistrm.cpp
3 // Purpose: streaming runtime metadata information
4 // Author: Stefan Csomor
8 // Copyright: (c) 2003 Stefan Csomor
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "xtistrm.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
25 #include "wx/object.h"
28 #include "wx/xml/xml.h"
29 #include "wx/tokenzr.h"
30 #include "wx/txtstrm.h"
33 #if wxUSE_EXTENDED_RTTI
35 #include "wx/xtistrm.h"
36 #include "wx/xtixml.h"
38 #include "wx/beforestd.h"
42 #include "wx/afterstd.h"
50 // convenience functions
52 void wxXmlAddContentToNode( wxXmlNode
* node
, const wxString
& data
)
54 node
->AddChild(new wxXmlNode(wxXML_TEXT_NODE
, "value", data
) );
57 wxString
wxXmlGetContentFromNode( wxXmlNode
*node
)
59 if ( node
->GetChildren() )
60 return node
->GetChildren()->GetContent() ;
62 return wxEmptyString
;
65 struct wxXmlWriter::wxXmlWriterInternal
68 wxXmlNode
*m_current
;
69 vector
< wxXmlNode
* > m_objectStack
;
71 void Push( wxXmlNode
*newCurrent
)
73 m_objectStack
.push_back( m_current
) ;
74 m_current
= newCurrent
;
79 m_current
= m_objectStack
.back() ;
80 m_objectStack
.pop_back() ;
84 wxXmlWriter::wxXmlWriter( wxXmlNode
* rootnode
)
86 m_data
= new wxXmlWriterInternal() ;
87 m_data
->m_root
= rootnode
;
88 m_data
->m_current
= rootnode
;
91 wxXmlWriter::~wxXmlWriter()
96 void wxXmlWriter::DoBeginWriteTopLevelEntry( const wxString
&name
)
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
) ;
105 void wxXmlWriter::DoEndWriteTopLevelEntry( const wxString
&WXUNUSED(name
) )
110 void wxXmlWriter::DoBeginWriteObject(const wxObject
*WXUNUSED(object
), const wxClassInfo
*classInfo
, int objectID
, wxxVariantArray
&metadata
)
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
) );
117 for ( size_t i
= 0 ; i
< metadata
.GetCount() ; ++i
)
119 pnode
->AddProperty( metadata
[i
].GetName() , metadata
[i
].GetAsString() ) ;
121 m_data
->m_current
->AddChild(pnode
) ;
122 m_data
->Push( pnode
) ;
125 // end of writing the root object
126 void wxXmlWriter::DoEndWriteObject(const wxObject
*WXUNUSED(object
), const wxClassInfo
*WXUNUSED(classInfo
), int WXUNUSED(objectID
) )
131 // writes a property in the stream format
132 void wxXmlWriter::DoWriteSimpleType( wxxVariant
&value
)
134 wxXmlAddContentToNode( m_data
->m_current
,value
.GetAsString() ) ;
137 void wxXmlWriter::DoBeginWriteElement()
140 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, "element" );
141 m_data
->m_current
->AddChild(pnode
) ;
142 m_data
->Push( pnode
) ;
145 void wxXmlWriter::DoEndWriteElement()
150 void wxXmlWriter::DoBeginWriteProperty(const wxPropertyInfo
*pi
)
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
) ;
159 void wxXmlWriter::DoEndWriteProperty(const wxPropertyInfo
*WXUNUSED(propInfo
) )
166 // insert an object reference to an already written object
167 void wxXmlWriter::DoWriteRepeatedObject( int objectID
)
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
) ;
175 // insert a null reference
176 void wxXmlWriter::DoWriteNullObject()
179 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
180 m_data
->m_current
->AddChild(pnode
) ;
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
)
187 if ( eventSink
!= NULL
&& handlerInfo
!= NULL
)
189 wxXmlAddContentToNode( m_data
->m_current
,wxString::Format(wxT("%d.%s"), sinkObjectID
, handlerInfo
->GetName().c_str()) ) ;
193 // ----------------------------------------------------------------------------
194 // reading objects in
195 // ----------------------------------------------------------------------------
199 // ----------------------------------------------------------------------------
201 // ----------------------------------------------------------------------------
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
209 int wxXmlReader::ReadComponent(wxXmlNode
*node
, wxDepersister
*callbacks
)
211 wxASSERT_MSG( callbacks
, wxT("Does not support reading without a Depersistor") ) ;
213 wxClassInfo
*classInfo
;
215 wxxVariant
*createParams
;
216 int *createParamOids
;
217 const wxClassInfo
** createClassInfos
;
220 wxString ObjectIdString
;
222 children
= node
->GetChildren();
225 // check for a null object or href
226 if (node
->GetPropVal("href" , &ObjectIdString
) )
228 objectID
= atoi( ObjectIdString
.c_str() ) ;
229 wxASSERT_MSG( HasObjectClassInfo( objectID
) , wxT("Forward hrefs are not supported") ) ;
232 if ( !node
->GetPropVal("id" , &ObjectIdString
) )
234 return wxNullObjectID
;
237 if (!node
->GetPropVal("class", &className
))
239 // No class name. Eek. FIXME: error handling
240 return wxInvalidObjectID
;
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
))
247 wxASSERT_MSG(0,wxT("Objects must have an id attribute") ) ;
248 // No object id. Eek. FIXME: error handling
249 return wxInvalidObjectID
;
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
) ) ;
255 // new object, start with allocation
256 // first make the object know to our internal registry
257 SetObjectClassInfo( objectID
, classInfo
) ;
259 wxxVariantArray metadata
;
260 wxXmlProperty
*xp
= node
->GetProperties() ;
263 if ( xp
->GetName() != wxString("class") && xp
->GetName() != wxString("id") )
265 metadata
.Add( new wxxVariant( xp
->GetValue() , xp
->GetName() ) ) ;
269 callbacks
->AllocateObject(objectID
, classInfo
, metadata
);
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() ] ;
277 typedef map
<string
, wxXmlNode
*> PropertyNodes
;
278 typedef vector
<string
> PropertyNames
;
280 PropertyNodes propertyNodes
;
281 PropertyNames propertyNames
;
286 children
->GetPropVal( wxT("name") , &name
) ;
287 propertyNames
.push_back( name
.c_str() ) ;
288 propertyNodes
[name
.c_str()] = children
->GetChildren() ;
289 children
= children
->GetNext() ;
292 for ( int i
= 0 ; i
<classInfo
->GetCreateParamCount() ; ++i
)
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() )
302 wxXmlNode
* prop
= propiter
->second
;
303 if ( pi
->GetTypeInfo()->IsObjectType() )
305 createParamOids
[i
] = ReadComponent( prop
, callbacks
) ;
306 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ;
310 createParamOids
[i
] = wxInvalidObjectID
;
311 createParams
[i
] = ReadValue( prop
, pi
->GetTypeInfo() ) ;
312 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
314 const wxEnumTypeInfo
*eti
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ;
315 wxASSERT_MSG( eti
, wxT("Type must have enum - long conversion") ) ;
318 eti
->ConvertToLong( createParams
[i
] , realval
) ;
319 createParams
[i
] = wxxVariant( realval
) ;
321 createClassInfos
[i
] = NULL
;
324 for ( size_t j
= 0 ; j
< propertyNames
.size() ; ++j
)
326 if ( propertyNames
[j
] == paramName
)
328 propertyNames
[j
] = "" ;
335 createParams
[i
] = pi
->GetDefaultValue() ;
339 // got the parameters. Call the Create method
340 callbacks
->CreateObject(objectID
, classInfo
,
341 classInfo
->GetCreateParamCount(),
342 createParams
, createParamOids
, createClassInfos
, metadata
);
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
)
347 if ( propertyNames
[j
].length() )
349 PropertyNodes::iterator propiter
= propertyNodes
.find( propertyNames
[j
] ) ;
350 if ( propiter
!= propertyNodes
.end() )
352 wxXmlNode
* prop
= propiter
->second
;
353 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() ) ;
354 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION
)
356 const wxCollectionTypeInfo
* collType
= dynamic_cast< const wxCollectionTypeInfo
* >( pi
->GetTypeInfo() ) ;
357 const wxTypeInfo
* elementType
= collType
->GetElementType() ;
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
)
364 // we skip empty elements
365 if ( elementType
->IsObjectType() )
367 int valueId
= ReadComponent( elementContent
, callbacks
) ;
368 if ( valueId
!= wxInvalidObjectID
)
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
) ) ;
379 wxxVariant elementValue
= ReadValue( elementContent
, elementType
) ;
380 if ( pi
->GetAccessor()->HasAdder() )
381 callbacks
->AddToPropertyCollection( objectID
, classInfo
,pi
, elementValue
) ;
384 prop
= prop
->GetNext() ;
387 else if ( pi
->GetTypeInfo()->IsObjectType() )
389 int valueId
= ReadComponent( prop
, callbacks
) ;
390 if ( valueId
!= wxInvalidObjectID
)
392 callbacks
->SetPropertyAsObject( objectID
, classInfo
, pi
, valueId
) ;
393 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
394 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) ) ;
397 else if ( pi
->GetTypeInfo()->IsDelegateType() )
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
) ;
408 callbacks
->SetConnect( objectID
, classInfo
, dynamic_cast<const wxDelegateTypeInfo
*>(pi
->GetTypeInfo()) , sinkClassInfo
,
409 sinkClassInfo
->FindHandlerInfo(handlerName
) , sinkOid
) ;
416 callbacks
->SetProperty( objectID
, classInfo
,pi
, ReadValue( prop
, pi
->GetTypeInfo() ) ) ;
422 delete[] createParams
;
423 delete[] createParamOids
;
424 delete[] createClassInfos
;
429 wxxVariant
wxXmlReader::ReadValue(wxXmlNode
*node
,
430 const wxTypeInfo
*type
)
434 content
= node
->GetContent() ;
436 type
->ConvertFromString( content
, result
) ;
440 int wxXmlReader::ReadObject( const wxString
&name
, wxDepersister
*callbacks
)
442 wxXmlNode
*iter
= m_parent
->GetChildren() ;
446 if ( iter
->GetPropVal("name", &entryName
) )
448 if ( entryName
== name
)
449 return ReadComponent( iter
->GetChildren() , callbacks
) ;
451 iter
= iter
->GetNext() ;
453 return wxInvalidObjectID
;