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/xtistrm.h"
31 #include "wx/xtixml.h"
32 #include "wx/txtstrm.h"
35 #if wxUSE_EXTENDED_RTTI
37 #include "wx/beforestd.h"
41 #include "wx/afterstd.h"
49 // convenience functions
51 void wxXmlAddContentToNode( wxXmlNode
* node
, const wxString
& data
)
53 node
->AddChild(new wxXmlNode(wxXML_TEXT_NODE
, "value", data
) );
56 wxString
wxXmlGetContentFromNode( wxXmlNode
*node
)
58 if ( node
->GetChildren() )
59 return node
->GetChildren()->GetContent() ;
61 return wxEmptyString
;
64 struct wxXmlWriter::wxXmlWriterInternal
67 wxXmlNode
*m_current
;
68 vector
< wxXmlNode
* > m_objectStack
;
70 void Push( wxXmlNode
*newCurrent
)
72 m_objectStack
.push_back( m_current
) ;
73 m_current
= newCurrent
;
78 m_current
= m_objectStack
.back() ;
79 m_objectStack
.pop_back() ;
83 wxXmlWriter::wxXmlWriter( wxXmlNode
* rootnode
)
85 m_data
= new wxXmlWriterInternal() ;
86 m_data
->m_root
= rootnode
;
87 m_data
->m_current
= rootnode
;
90 wxXmlWriter::~wxXmlWriter()
95 void wxXmlWriter::DoBeginWriteTopLevelEntry( const wxString
&name
)
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
) ;
104 void wxXmlWriter::DoEndWriteTopLevelEntry( const wxString
&WXUNUSED(name
) )
109 void wxXmlWriter::DoBeginWriteObject(const wxObject
*WXUNUSED(object
), const wxClassInfo
*classInfo
, int objectID
, wxxVariantArray
&metadata
)
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
) );
116 for ( size_t i
= 0 ; i
< metadata
.GetCount() ; ++i
)
118 pnode
->AddProperty( metadata
[i
].GetName() , metadata
[i
].GetAsString() ) ;
120 m_data
->m_current
->AddChild(pnode
) ;
121 m_data
->Push( pnode
) ;
124 // end of writing the root object
125 void wxXmlWriter::DoEndWriteObject(const wxObject
*WXUNUSED(object
), const wxClassInfo
*WXUNUSED(classInfo
), int WXUNUSED(objectID
) )
130 // writes a property in the stream format
131 void wxXmlWriter::DoWriteSimpleType( wxxVariant
&value
)
133 wxXmlAddContentToNode( m_data
->m_current
,value
.GetAsString() ) ;
136 void wxXmlWriter::DoBeginWriteElement()
139 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, "element" );
140 m_data
->m_current
->AddChild(pnode
) ;
141 m_data
->Push( pnode
) ;
144 void wxXmlWriter::DoEndWriteElement()
149 void wxXmlWriter::DoBeginWriteProperty(const wxPropertyInfo
*pi
)
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
) ;
158 void wxXmlWriter::DoEndWriteProperty(const wxPropertyInfo
*WXUNUSED(propInfo
) )
165 // insert an object reference to an already written object
166 void wxXmlWriter::DoWriteRepeatedObject( int objectID
)
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
) ;
174 // insert a null reference
175 void wxXmlWriter::DoWriteNullObject()
178 pnode
= new wxXmlNode(wxXML_ELEMENT_NODE
, wxT("object"));
179 m_data
->m_current
->AddChild(pnode
) ;
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
)
186 if ( eventSink
!= NULL
&& handlerInfo
!= NULL
)
188 wxXmlAddContentToNode( m_data
->m_current
,wxString::Format(wxT("%d.%s"), sinkObjectID
, handlerInfo
->GetName()) ) ;
192 // ----------------------------------------------------------------------------
193 // reading objects in
194 // ----------------------------------------------------------------------------
198 // ----------------------------------------------------------------------------
200 // ----------------------------------------------------------------------------
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
208 int wxXmlReader::ReadComponent(wxXmlNode
*node
, wxDepersister
*callbacks
)
210 wxASSERT_MSG( callbacks
, wxT("Does not support reading without a Depersistor") ) ;
212 wxClassInfo
*classInfo
;
214 wxxVariant
*createParams
;
215 int *createParamOids
;
216 const wxClassInfo
** createClassInfos
;
219 wxString ObjectIdString
;
221 children
= node
->GetChildren();
224 // check for a null object or href
225 if (node
->GetPropVal("href" , &ObjectIdString
) )
227 objectID
= atoi( ObjectIdString
.c_str() ) ;
228 wxASSERT_MSG( HasObjectClassInfo( objectID
) , wxT("Forward hrefs are not supported") ) ;
231 if ( !node
->GetPropVal("id" , &ObjectIdString
) )
233 return wxNullObjectID
;
236 if (!node
->GetPropVal("class", &className
))
238 // No class name. Eek. FIXME: error handling
239 return wxInvalidObjectID
;
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
))
246 wxASSERT_MSG(0,wxT("Objects must have an id attribute") ) ;
247 // No object id. Eek. FIXME: error handling
248 return wxInvalidObjectID
;
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
) ) ;
254 // new object, start with allocation
255 // first make the object know to our internal registry
256 SetObjectClassInfo( objectID
, classInfo
) ;
258 wxxVariantArray metadata
;
259 wxXmlProperty
*xp
= node
->GetProperties() ;
262 if ( xp
->GetName() != wxString("class") && xp
->GetName() != wxString("id") )
264 metadata
.Add( new wxxVariant( xp
->GetValue() , xp
->GetName() ) ) ;
268 callbacks
->AllocateObject(objectID
, classInfo
, metadata
);
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() ] ;
276 typedef map
<string
, wxXmlNode
*> PropertyNodes
;
277 typedef vector
<string
> PropertyNames
;
279 PropertyNodes propertyNodes
;
280 PropertyNames propertyNames
;
285 children
->GetPropVal( wxT("name") , &name
) ;
286 propertyNames
.push_back( name
.c_str() ) ;
287 propertyNodes
[name
.c_str()] = children
->GetChildren() ;
288 children
= children
->GetNext() ;
291 for ( int i
= 0 ; i
<classInfo
->GetCreateParamCount() ; ++i
)
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() )
301 wxXmlNode
* prop
= propiter
->second
;
302 if ( pi
->GetTypeInfo()->IsObjectType() )
304 createParamOids
[i
] = ReadComponent( prop
, callbacks
) ;
305 createClassInfos
[i
] = dynamic_cast<const wxClassTypeInfo
*>(pi
->GetTypeInfo())->GetClassInfo() ;
309 createParamOids
[i
] = wxInvalidObjectID
;
310 createParams
[i
] = ReadValue( prop
, pi
->GetTypeInfo() ) ;
311 if( pi
->GetFlags() & wxPROP_ENUM_STORE_LONG
)
313 const wxEnumTypeInfo
*eti
= dynamic_cast<const wxEnumTypeInfo
*>( pi
->GetTypeInfo() ) ;
314 wxASSERT_MSG( eti
, wxT("Type must have enum - long conversion") ) ;
317 eti
->ConvertToLong( createParams
[i
] , realval
) ;
318 createParams
[i
] = wxxVariant( realval
) ;
320 createClassInfos
[i
] = NULL
;
323 for ( size_t j
= 0 ; j
< propertyNames
.size() ; ++j
)
325 if ( propertyNames
[j
] == paramName
)
327 propertyNames
[j
] = "" ;
334 createParams
[i
] = pi
->GetDefaultValue() ;
338 // got the parameters. Call the Create method
339 callbacks
->CreateObject(objectID
, classInfo
,
340 classInfo
->GetCreateParamCount(),
341 createParams
, createParamOids
, createClassInfos
, metadata
);
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
)
346 if ( propertyNames
[j
].length() )
348 PropertyNodes::iterator propiter
= propertyNodes
.find( propertyNames
[j
] ) ;
349 if ( propiter
!= propertyNodes
.end() )
351 wxXmlNode
* prop
= propiter
->second
;
352 const wxPropertyInfo
* pi
= classInfo
->FindPropertyInfo( propertyNames
[j
].c_str() ) ;
353 if ( pi
->GetTypeInfo()->GetKind() == wxT_COLLECTION
)
355 const wxCollectionTypeInfo
* collType
= dynamic_cast< const wxCollectionTypeInfo
* >( pi
->GetTypeInfo() ) ;
356 const wxTypeInfo
* elementType
= collType
->GetElementType() ;
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
)
363 // we skip empty elements
364 if ( elementType
->IsObjectType() )
366 int valueId
= ReadComponent( elementContent
, callbacks
) ;
367 if ( valueId
!= wxInvalidObjectID
)
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
) ) ;
378 wxxVariant elementValue
= ReadValue( elementContent
, elementType
) ;
379 if ( pi
->GetAccessor()->HasAdder() )
380 callbacks
->AddToPropertyCollection( objectID
, classInfo
,pi
, elementValue
) ;
383 prop
= prop
->GetNext() ;
386 else if ( pi
->GetTypeInfo()->IsObjectType() )
388 int valueId
= ReadComponent( prop
, callbacks
) ;
389 if ( valueId
!= wxInvalidObjectID
)
391 callbacks
->SetPropertyAsObject( objectID
, classInfo
, pi
, valueId
) ;
392 if ( pi
->GetTypeInfo()->GetKind() == wxT_OBJECT
&& valueId
!= wxNullObjectID
)
393 callbacks
->DestroyObject( valueId
, GetObjectClassInfo( valueId
) ) ;
396 else if ( pi
->GetTypeInfo()->IsDelegateType() )
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
) ;
407 callbacks
->SetConnect( objectID
, classInfo
, dynamic_cast<const wxDelegateTypeInfo
*>(pi
->GetTypeInfo()) , sinkClassInfo
,
408 sinkClassInfo
->FindHandlerInfo(handlerName
) , sinkOid
) ;
415 callbacks
->SetProperty( objectID
, classInfo
,pi
, ReadValue( prop
, pi
->GetTypeInfo() ) ) ;
421 delete[] createParams
;
422 delete[] createParamOids
;
423 delete[] createClassInfos
;
428 wxxVariant
wxXmlReader::ReadValue(wxXmlNode
*node
,
429 const wxTypeInfo
*type
)
433 content
= node
->GetContent() ;
435 type
->ConvertFromString( content
, result
) ;
439 int wxXmlReader::ReadObject( const wxString
&name
, wxDepersister
*callbacks
)
441 wxXmlNode
*iter
= m_parent
->GetChildren() ;
445 if ( iter
->GetPropVal("name", &entryName
) )
447 if ( entryName
== name
)
448 return ReadComponent( iter
->GetChildren() , callbacks
) ;
450 iter
= iter
->GetNext() ;
452 return wxInvalidObjectID
;