2015-07-29 14:19:43 +02:00
# include "cafPdmXmlObjectHandle.h"
2017-03-08 08:19:51 +01:00
# include "cafAssert.h"
2015-07-29 14:19:43 +02:00
# include "cafPdmObjectHandle.h"
# include "cafPdmXmlFieldHandle.h"
# include "cafPdmFieldHandle.h"
# include <QXmlStreamReader>
# include <QXmlStreamWriter>
# include <iostream>
namespace caf
{
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
PdmXmlObjectHandle : : PdmXmlObjectHandle ( PdmObjectHandle * owner , bool giveOwnership )
{
m_owner = owner ;
m_owner - > addCapability ( this , giveOwnership ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
PdmXmlObjectHandle * xmlObj ( PdmObjectHandle * obj )
{
2018-02-18 18:05:08 +01:00
if ( ! obj ) return nullptr ;
2015-07-29 14:19:43 +02:00
PdmXmlObjectHandle * xmlObject = obj - > capability < PdmXmlObjectHandle > ( ) ;
2017-03-08 08:19:51 +01:00
CAF_ASSERT ( xmlObject ) ;
2015-07-29 14:19:43 +02:00
return xmlObject ;
}
//--------------------------------------------------------------------------------------------------
/// Reads all the fields into this PdmObject
/// Assumes xmlStream points to the start element token of the PdmObject for which to read fields.
/// ( and not first token of object content)
/// This makes attribute based field storage possible.
/// Leaves the xmlStream pointing to the EndElement of the PdmObject.
//--------------------------------------------------------------------------------------------------
void PdmXmlObjectHandle : : readFields ( QXmlStreamReader & xmlStream , PdmObjectFactory * objectFactory )
{
bool isObjectFinished = false ;
QXmlStreamReader : : TokenType type ;
while ( ! isObjectFinished )
{
type = xmlStream . readNext ( ) ;
switch ( type )
{
case QXmlStreamReader : : StartElement :
{
QString name = xmlStream . name ( ) . toString ( ) ;
PdmFieldHandle * fieldHandle = m_owner - > findField ( name ) ;
2015-10-23 15:21:23 +02:00
if ( fieldHandle & & fieldHandle - > xmlCapability ( ) )
2015-07-29 14:19:43 +02:00
{
2015-10-23 15:21:23 +02:00
PdmXmlFieldHandle * xmlFieldHandle = fieldHandle - > xmlCapability ( ) ;
2015-07-29 14:19:43 +02:00
if ( xmlFieldHandle - > isIOReadable ( ) )
{
// readFieldData assumes that the xmlStream points to first token of field content.
// After reading, the xmlStream is supposed to point to the first token after the field content.
// (typically an "endElement")
QXmlStreamReader : : TokenType tt ;
tt = xmlStream . readNext ( ) ;
xmlFieldHandle - > readFieldData ( xmlStream , objectFactory ) ;
}
else
{
xmlStream . skipCurrentElement ( ) ;
}
}
else
{
std : : cout < < " Line " < < xmlStream . lineNumber ( ) < < " : Warning: Could not find a field with name " < < name . toLatin1 ( ) . data ( ) < < " in the current object : " < < classKeyword ( ) . toLatin1 ( ) . data ( ) < < std : : endl ;
xmlStream . skipCurrentElement ( ) ;
}
break ;
}
break ;
case QXmlStreamReader : : EndElement :
{
// End of object.
QString name = xmlStream . name ( ) . toString ( ) ; // For debugging
isObjectFinished = true ;
}
break ;
case QXmlStreamReader : : EndDocument :
{
// End of object.
isObjectFinished = true ;
}
break ;
default :
{
// Just read on
// Todo: Error handling
}
break ;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2016-12-09 21:04:16 +01:00
void PdmXmlObjectHandle : : writeFields ( QXmlStreamWriter & xmlStream ) const
2015-07-29 14:19:43 +02:00
{
std : : vector < PdmFieldHandle * > fields ;
m_owner - > fields ( fields ) ;
for ( size_t it = 0 ; it < fields . size ( ) ; + + it )
{
2016-12-09 21:04:16 +01:00
const PdmXmlFieldHandle * field = fields [ it ] - > xmlCapability ( ) ;
2015-07-29 14:19:43 +02:00
if ( field & & field - > isIOWritable ( ) )
{
QString keyword = field - > fieldHandle ( ) - > keyword ( ) ;
2017-03-08 08:19:51 +01:00
CAF_ASSERT ( PdmXmlObjectHandle : : isValidXmlElementName ( keyword ) ) ;
2015-07-29 14:19:43 +02:00
xmlStream . writeStartElement ( " " , keyword ) ;
field - > writeFieldData ( xmlStream ) ;
xmlStream . writeEndElement ( ) ;
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmXmlObjectHandle : : readObjectFromXmlString ( const QString & xmlString , PdmObjectFactory * objectFactory )
{
// A valid XML is used to store field data of the object. The format of the XML is like this
/*
< classKeyword >
< fieldKeywordA > value < / fieldKeywordA >
< fieldKeywordB > value < / fieldKeywordB >
< / classKeyword >
*/
QXmlStreamReader inputStream ( xmlString ) ;
QXmlStreamReader : : TokenType tt ;
tt = inputStream . readNext ( ) ; // Start of document
tt = inputStream . readNext ( ) ;
QString classKeyword = inputStream . name ( ) . toString ( ) ;
2017-03-08 08:19:51 +01:00
CAF_ASSERT ( classKeyword = = this - > classKeyword ( ) ) ;
2015-07-29 14:19:43 +02:00
this - > readFields ( inputStream , objectFactory ) ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
PdmObjectHandle * PdmXmlObjectHandle : : readUnknownObjectFromXmlString ( const QString & xmlString , PdmObjectFactory * objectFactory )
{
QXmlStreamReader inputStream ( xmlString ) ;
QXmlStreamReader : : TokenType tt ;
tt = inputStream . readNext ( ) ; // Start of document
tt = inputStream . readNext ( ) ;
QString classKeyword = inputStream . name ( ) . toString ( ) ;
PdmObjectHandle * newObject = objectFactory - > create ( classKeyword ) ;
xmlObj ( newObject ) - > readFields ( inputStream , objectFactory ) ;
return newObject ;
}
2015-10-23 09:00:17 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
PdmObjectHandle * PdmXmlObjectHandle : : copyByXmlSerialization ( PdmObjectFactory * objectFactory )
{
this - > setupBeforeSaveRecursively ( ) ;
QString xmlString = this - > writeObjectToXmlString ( ) ;
PdmObjectHandle * objectCopy = PdmXmlObjectHandle : : readUnknownObjectFromXmlString ( xmlString , objectFactory ) ;
objectCopy - > xmlCapability ( ) - > initAfterReadRecursively ( ) ;
return objectCopy ;
}
2018-10-23 16:32:40 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
caf : : PdmObjectHandle * PdmXmlObjectHandle : : copyAndCastByXmlSerialization ( const QString & destinationClassKeyword , const QString & sourceClassKeyword , PdmObjectFactory * objectFactory )
{
this - > setupBeforeSaveRecursively ( ) ;
QString xmlString = this - > writeObjectToXmlString ( ) ;
PdmObjectHandle * upgradedObject = objectFactory - > create ( destinationClassKeyword ) ;
QXmlStreamReader inputStream ( xmlString ) ;
QXmlStreamReader : : TokenType tt ;
tt = inputStream . readNext ( ) ; // Start of document
tt = inputStream . readNext ( ) ;
QString classKeyword = inputStream . name ( ) . toString ( ) ;
CAF_ASSERT ( classKeyword = = sourceClassKeyword ) ;
xmlObj ( upgradedObject ) - > readFields ( inputStream , objectFactory ) ;
return upgradedObject ;
}
2015-07-29 14:19:43 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2016-12-09 21:04:16 +01:00
QString PdmXmlObjectHandle : : writeObjectToXmlString ( ) const
2015-07-29 14:19:43 +02:00
{
QString xmlString ;
QXmlStreamWriter outputStream ( & xmlString ) ;
outputStream . setAutoFormatting ( true ) ;
outputStream . writeStartElement ( " " , this - > classKeyword ( ) ) ;
this - > writeFields ( outputStream ) ;
outputStream . writeEndElement ( ) ;
return xmlString ;
}
//--------------------------------------------------------------------------------------------------
/// Check if a string is a valid Xml element name
//
/// http://www.w3schools.com/xml/xml_elements.asp
///
/// XML elements must follow these naming rules:
/// Names can contain letters, numbers, and other characters
/// Names cannot start with a number or punctuation character
/// Names cannot start with the letters xml (or XML, or Xml, etc)
/// Names cannot contain spaces
//--------------------------------------------------------------------------------------------------
bool PdmXmlObjectHandle : : isValidXmlElementName ( const QString & name )
{
if ( name . isEmpty ( ) )
{
return false ;
}
if ( name . size ( ) > 0 )
{
QChar firstChar = name [ 0 ] ;
if ( firstChar . isDigit ( ) | | firstChar = = ' . ' )
{
return false ;
}
}
if ( name . size ( ) > = 3 )
{
if ( name . left ( 3 ) . compare ( " xml " , Qt : : CaseInsensitive ) = = 0 )
{
return false ;
}
}
if ( name . contains ( ' ' ) )
{
return false ;
}
return true ;
}
2015-08-27 11:43:27 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmXmlObjectHandle : : initAfterReadRecursively ( PdmObjectHandle * object )
{
2018-02-18 18:05:08 +01:00
if ( object = = nullptr ) return ;
2015-08-27 11:43:27 +02:00
std : : vector < PdmFieldHandle * > fields ;
object - > fields ( fields ) ;
std : : vector < PdmObjectHandle * > children ;
size_t fIdx ;
for ( fIdx = 0 ; fIdx < fields . size ( ) ; + + fIdx )
{
if ( fields [ fIdx ] ) fields [ fIdx ] - > childObjects ( & children ) ;
}
size_t cIdx ;
for ( cIdx = 0 ; cIdx < children . size ( ) ; + + cIdx )
{
initAfterReadRecursively ( children [ cIdx ] ) ;
}
PdmXmlObjectHandle * xmlObject = xmlObj ( object ) ;
if ( xmlObject )
{
xmlObject - > initAfterRead ( ) ;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmXmlObjectHandle : : resolveReferencesRecursively ( PdmObjectHandle * object )
{
2018-02-18 18:05:08 +01:00
if ( object = = nullptr ) return ;
2015-08-27 11:43:27 +02:00
std : : vector < PdmFieldHandle * > fields ;
object - > fields ( fields ) ;
std : : vector < PdmObjectHandle * > children ;
size_t fIdx ;
for ( fIdx = 0 ; fIdx < fields . size ( ) ; + + fIdx )
{
PdmFieldHandle * field = fields [ fIdx ] ;
if ( field )
{
field - > childObjects ( & children ) ;
field - > xmlCapability ( ) - > resolveReferences ( ) ;
}
}
size_t cIdx ;
for ( cIdx = 0 ; cIdx < children . size ( ) ; + + cIdx )
{
resolveReferencesRecursively ( children [ cIdx ] ) ;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void PdmXmlObjectHandle : : setupBeforeSaveRecursively ( PdmObjectHandle * object )
{
2018-02-18 18:05:08 +01:00
if ( object = = nullptr ) return ;
2015-08-27 11:43:27 +02:00
std : : vector < PdmFieldHandle * > fields ;
object - > fields ( fields ) ;
std : : vector < PdmObjectHandle * > children ;
size_t fIdx ;
for ( fIdx = 0 ; fIdx < fields . size ( ) ; + + fIdx )
{
if ( fields [ fIdx ] ) fields [ fIdx ] - > childObjects ( & children ) ;
}
size_t cIdx ;
for ( cIdx = 0 ; cIdx < children . size ( ) ; + + cIdx )
{
setupBeforeSaveRecursively ( children [ cIdx ] ) ;
}
PdmXmlObjectHandle * xmlObject = xmlObj ( object ) ;
if ( xmlObject )
{
xmlObject - > setupBeforeSave ( ) ;
}
}
2015-07-29 14:19:43 +02:00
2015-08-20 15:22:43 +02:00
//--------------------------------------------------------------------------------------------------
/// Implementation of xmlCapability() defined in cafPdmObjectHandle.h
//--------------------------------------------------------------------------------------------------
2016-08-24 15:16:34 +02:00
PdmXmlObjectHandle * PdmObjectHandle : : xmlCapability ( ) const
2015-08-20 15:22:43 +02:00
{
PdmXmlObjectHandle * xmlField = capability < PdmXmlObjectHandle > ( ) ;
2017-03-08 08:19:51 +01:00
CAF_ASSERT ( xmlField ) ;
2015-08-20 15:22:43 +02:00
return xmlField ;
}
2015-07-29 14:19:43 +02:00
} // end namespace caf