Files
ResInsight/Fwk/AppFwk/cafProjectDataModel/cafPdmXml/cafPdmXmlObjectHandle.cpp

467 lines
17 KiB
C++
Raw Normal View History

#include "cafPdmXmlObjectHandle.h"
#include "cafAssert.h"
#include "cafPdmObjectHandle.h"
#include "cafPdmXmlFieldHandle.h"
#include "cafPdmFieldHandle.h"
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <iostream>
namespace caf
{
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
PdmXmlObjectHandle::PdmXmlObjectHandle( PdmObjectHandle* owner, bool giveOwnership )
{
m_owner = owner;
2020-06-19 07:53:59 +02:00
m_owner->addCapability( this, giveOwnership );
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
PdmXmlObjectHandle* xmlObj( PdmObjectHandle* obj )
{
2020-06-19 07:53:59 +02:00
if ( !obj ) return nullptr;
PdmXmlObjectHandle* xmlObject = obj->capability<PdmXmlObjectHandle>();
2020-06-19 07:53:59 +02:00
CAF_ASSERT( xmlObject );
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.
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
void PdmXmlObjectHandle::readFields( QXmlStreamReader& xmlStream, PdmObjectFactory* objectFactory, bool isCopyOperation )
{
2020-06-19 07:53:59 +02:00
bool isObjectFinished = false;
QXmlStreamReader::TokenType type;
2020-06-19 07:53:59 +02:00
while ( !isObjectFinished )
{
type = xmlStream.readNext();
2020-06-19 07:53:59 +02:00
switch ( type )
{
2020-06-19 07:53:59 +02:00
case QXmlStreamReader::StartElement:
{
2020-06-19 07:53:59 +02:00
QString name = xmlStream.name().toString();
PdmFieldHandle* fieldHandle = m_owner->findField( name );
if ( fieldHandle && fieldHandle->xmlCapability() )
{
auto xmlAttributes = xmlStream.attributes();
if ( !xmlAttributes.isEmpty() )
{
std::vector<std::pair<QString, QString>> fieldAttributes;
for ( const auto& xmlAttr : xmlAttributes )
{
fieldAttributes.emplace_back( xmlAttr.name().toString(), xmlAttr.value().toString() );
}
for ( auto capability : fieldHandle->capabilities() )
{
capability->setAttributes( fieldAttributes );
}
}
2020-06-19 07:53:59 +02:00
PdmXmlFieldHandle* xmlFieldHandle = fieldHandle->xmlCapability();
bool readable = xmlFieldHandle->isIOReadable();
if ( isCopyOperation && !xmlFieldHandle->isCopyable() )
{
readable = false;
}
if ( readable )
{
// 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")
2020-06-19 07:53:59 +02:00
QXmlStreamReader::TokenType tt;
tt = xmlStream.readNext();
xmlFieldHandle->readFieldData( xmlStream, objectFactory );
}
else
{
xmlStream.skipCurrentElement();
}
}
else
{
2020-06-19 07:53:59 +02:00
// Debug text is commented out, as this code is relatively often reached. Consider a new logging
// concept to receive this information
//
// 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;
2020-06-19 07:53:59 +02:00
xmlStream.skipCurrentElement();
}
2020-06-19 07:53:59 +02:00
break;
}
2020-06-19 07:53:59 +02:00
break;
case QXmlStreamReader::EndElement:
{
2020-06-19 07:53:59 +02:00
// End of object.
QString name = xmlStream.name().toString(); // For debugging
isObjectFinished = true;
}
break;
2020-06-19 07:53:59 +02:00
case QXmlStreamReader::EndDocument:
{
// End of object.
isObjectFinished = true;
}
break;
2020-06-19 07:53:59 +02:00
default:
{
// Just read on
// Todo: Error handling
}
break;
}
}
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
void PdmXmlObjectHandle::writeFields( QXmlStreamWriter& xmlStream ) const
{
std::vector<PdmFieldHandle*> fields;
2020-06-19 07:53:59 +02:00
m_owner->fields( fields );
for ( const auto& fieldHandle : fields )
{
const PdmXmlFieldHandle* field = fieldHandle->xmlCapability();
2020-06-19 07:53:59 +02:00
if ( field && field->isIOWritable() )
{
QString keyword = field->fieldHandle()->keyword();
2020-06-19 07:53:59 +02:00
CAF_ASSERT( PdmXmlObjectHandle::isValidXmlElementName( keyword ) );
2020-06-19 07:53:59 +02:00
xmlStream.writeStartElement( "", keyword );
for ( auto cap : fieldHandle->capabilities() )
{
auto attributes = cap->attributes();
for ( const auto& [key, value] : attributes )
{
xmlStream.writeAttribute( key, value );
}
}
2020-06-19 07:53:59 +02:00
field->writeFieldData( xmlStream );
xmlStream.writeEndElement();
}
}
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
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>
*/
2020-06-19 07:53:59 +02:00
QXmlStreamReader inputStream( xmlString );
QXmlStreamReader::TokenType tt;
2020-06-19 07:53:59 +02:00
tt = inputStream.readNext(); // Start of document
tt = inputStream.readNext();
QString classKeyword = inputStream.name().toString();
2020-06-19 07:53:59 +02:00
CAF_ASSERT( classKeyword == this->classKeyword() );
this->readFields( inputStream, objectFactory, false );
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
PdmObjectHandle* PdmXmlObjectHandle::readUnknownObjectFromXmlString( const QString& xmlString,
PdmObjectFactory* objectFactory,
bool isCopyOperation )
{
2020-06-19 07:53:59 +02:00
QXmlStreamReader inputStream( xmlString );
QXmlStreamReader::TokenType tt;
2020-06-19 07:53:59 +02:00
tt = inputStream.readNext(); // Start of document
tt = inputStream.readNext();
QString classKeyword = inputStream.name().toString();
PdmObjectHandle* newObject = objectFactory->create( classKeyword );
2020-06-19 07:53:59 +02:00
if ( !newObject ) return nullptr;
2019-09-17 11:45:05 +02:00
2020-06-19 07:53:59 +02:00
xmlObj( newObject )->readFields( inputStream, objectFactory, isCopyOperation );
return newObject;
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
PdmObjectHandle* PdmXmlObjectHandle::copyByXmlSerialization( PdmObjectFactory* objectFactory )
{
this->setupBeforeSaveRecursively();
QString xmlString = this->writeObjectToXmlString();
2020-06-19 07:53:59 +02:00
PdmObjectHandle* objectCopy = PdmXmlObjectHandle::readUnknownObjectFromXmlString( xmlString, objectFactory, true );
if ( !objectCopy ) return nullptr;
2019-09-17 11:45:05 +02:00
objectCopy->xmlCapability()->initAfterReadRecursively();
return objectCopy;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
caf::PdmObjectHandle* PdmXmlObjectHandle::copyAndCastByXmlSerialization( const QString& destinationClassKeyword,
const QString& sourceClassKeyword,
PdmObjectFactory* objectFactory )
{
this->setupBeforeSaveRecursively();
QString xmlString = this->writeObjectToXmlString();
2020-06-19 07:53:59 +02:00
PdmObjectHandle* upgradedObject = objectFactory->create( destinationClassKeyword );
if ( !upgradedObject ) return nullptr;
QXmlStreamReader inputStream( xmlString );
QXmlStreamReader::TokenType tt;
2020-06-19 07:53:59 +02:00
tt = inputStream.readNext(); // Start of document
tt = inputStream.readNext();
QString classKeyword = inputStream.name().toString();
2020-06-19 07:53:59 +02:00
CAF_ASSERT( classKeyword == sourceClassKeyword );
2020-06-19 07:53:59 +02:00
xmlObj( upgradedObject )->readFields( inputStream, objectFactory, true );
2019-11-14 20:48:11 +01:00
2020-06-19 07:53:59 +02:00
xmlObj( upgradedObject )->initAfterReadRecursively();
return upgradedObject;
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
QString PdmXmlObjectHandle::writeObjectToXmlString() const
{
2020-06-19 07:53:59 +02:00
QString xmlString;
QXmlStreamWriter outputStream( &xmlString );
outputStream.setAutoFormatting( true );
2020-06-19 07:53:59 +02:00
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
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
bool PdmXmlObjectHandle::isValidXmlElementName( const QString& name )
{
2020-06-19 07:53:59 +02:00
if ( name.isEmpty() )
{
return false;
}
2020-06-19 07:53:59 +02:00
if ( name.size() > 0 )
{
QChar firstChar = name[0];
2020-06-19 07:53:59 +02:00
if ( firstChar.isDigit() || firstChar == '.' )
{
return false;
}
}
2020-06-19 07:53:59 +02:00
if ( name.size() >= 3 )
{
2020-06-19 07:53:59 +02:00
if ( name.left( 3 ).compare( "xml", Qt::CaseInsensitive ) == 0 )
{
return false;
}
}
2020-06-19 07:53:59 +02:00
if ( name.contains( ' ' ) )
{
return false;
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
void PdmXmlObjectHandle::registerClassKeyword( const QString& registerKeyword )
{
2020-06-19 07:53:59 +02:00
m_classInheritanceStack.push_back( registerKeyword );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
bool PdmXmlObjectHandle::inheritsClassWithKeyword( const QString& testClassKeyword ) const
{
2020-06-19 07:53:59 +02:00
return std::find( m_classInheritanceStack.begin(), m_classInheritanceStack.end(), testClassKeyword ) !=
m_classInheritanceStack.end();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
const std::list<QString>& PdmXmlObjectHandle::classInheritanceStack() const
{
return m_classInheritanceStack;
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
void PdmXmlObjectHandle::initAfterReadRecursively( PdmObjectHandle* object )
{
2020-06-19 07:53:59 +02:00
if ( object == nullptr ) return;
std::vector<PdmFieldHandle*> fields;
2020-06-19 07:53:59 +02:00
object->fields( fields );
std::vector<PdmObjectHandle*> children;
2020-06-19 07:53:59 +02:00
size_t fIdx;
for ( fIdx = 0; fIdx < fields.size(); ++fIdx )
{
if ( fields[fIdx] ) fields[fIdx]->children( &children );
}
size_t cIdx;
2020-06-19 07:53:59 +02:00
for ( cIdx = 0; cIdx < children.size(); ++cIdx )
{
2020-06-19 07:53:59 +02:00
initAfterReadRecursively( children[cIdx] );
}
2020-06-19 07:53:59 +02:00
PdmXmlObjectHandle* xmlObject = xmlObj( object );
if ( xmlObject )
{
xmlObject->initAfterRead();
}
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
void PdmXmlObjectHandle::resolveReferencesRecursively( PdmObjectHandle* object,
std::vector<PdmFieldHandle*>* fieldWithFailingResolve )
{
2020-06-19 07:53:59 +02:00
if ( object == nullptr ) return;
std::vector<PdmFieldHandle*> fields;
2020-06-19 07:53:59 +02:00
object->fields( fields );
std::vector<PdmObjectHandle*> children;
2020-06-19 07:53:59 +02:00
size_t fIdx;
for ( fIdx = 0; fIdx < fields.size(); ++fIdx )
{
PdmFieldHandle* field = fields[fIdx];
2020-06-19 07:53:59 +02:00
if ( field )
{
field->children( &children );
bool resolvedOk = field->xmlCapability()->resolveReferences();
2020-06-19 07:53:59 +02:00
if ( fieldWithFailingResolve && !resolvedOk )
{
2020-06-19 07:53:59 +02:00
fieldWithFailingResolve->push_back( field );
}
}
}
size_t cIdx;
2020-06-19 07:53:59 +02:00
for ( cIdx = 0; cIdx < children.size(); ++cIdx )
{
2020-06-19 07:53:59 +02:00
resolveReferencesRecursively( children[cIdx], fieldWithFailingResolve );
}
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
void PdmXmlObjectHandle::resolveReferencesRecursively(
std::vector<PdmFieldHandle*>* fieldWithFailingResolve /*= nullptr*/ )
{
std::vector<PdmFieldHandle*> tempFields;
2020-06-19 07:53:59 +02:00
resolveReferencesRecursively( this->m_owner, &tempFields );
2020-06-19 07:53:59 +02:00
if ( fieldWithFailingResolve )
{
2020-06-19 07:53:59 +02:00
for ( auto f : tempFields )
{
2020-06-19 07:53:59 +02:00
fieldWithFailingResolve->push_back( f );
}
}
}
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
///
//--------------------------------------------------------------------------------------------------
2020-06-19 07:53:59 +02:00
void PdmXmlObjectHandle::setupBeforeSaveRecursively( PdmObjectHandle* object )
{
2020-06-19 07:53:59 +02:00
if ( object == nullptr ) return;
std::vector<PdmFieldHandle*> fields;
2020-06-19 07:53:59 +02:00
object->fields( fields );
std::vector<PdmObjectHandle*> children;
2020-06-19 07:53:59 +02:00
size_t fIdx;
for ( fIdx = 0; fIdx < fields.size(); ++fIdx )
{
if ( fields[fIdx] ) fields[fIdx]->children( &children );
}
size_t cIdx;
2020-06-19 07:53:59 +02:00
for ( cIdx = 0; cIdx < children.size(); ++cIdx )
{
2020-06-19 07:53:59 +02:00
setupBeforeSaveRecursively( children[cIdx] );
}
2020-06-19 07:53:59 +02:00
PdmXmlObjectHandle* xmlObject = xmlObj( object );
if ( xmlObject )
{
xmlObject->setupBeforeSave();
}
}
//--------------------------------------------------------------------------------------------------
/// Implementation of xmlCapability() defined in cafPdmObjectHandle.h
//--------------------------------------------------------------------------------------------------
PdmXmlObjectHandle* PdmObjectHandle::xmlCapability() const
2020-06-19 07:53:59 +02:00
{
PdmXmlObjectHandle* xmlField = capability<PdmXmlObjectHandle>();
2020-06-19 07:53:59 +02:00
CAF_ASSERT( xmlField );
return xmlField;
}
} // end namespace caf