ResInsight/cafProjectDataModel/cafPdmFieldImpl.h
2012-05-18 09:45:23 +02:00

449 lines
16 KiB
C++

//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2012 Ceetron AS
//
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
//##################################################################################################
#pragma once
#include <QVariant>
#include <QList>
#include <QStringList>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QTextStream>
#include "cafPdmUiItem.h"
#include "cafPdmObjectFactory.h"
namespace caf
{
// Forward declarations
template <typename T> class PdmField;
template <typename T> class PdmPointer;
template <typename T> class AppEnum;
//==================================================================================================
/// A proxy class that implements the Gui interface of fields
///
/// This class collects methods that need specialization when introducing a new type in a PdmField.
/// Having those methods in a separate class makes it possible to "partially specialize" the methods
/// for container classes etc. since partial specialization of template functions is not C++ as of yet.
///
/// When introducing a new type in a PdmField, you might need to implement a (partial)specialization
/// of this class.
//==================================================================================================
template <typename T>
class PdmFieldTypeSpecialization
{
public:
/// Convert the field value into a QVariant
static QVariant convert(const T& value)
{
return QVariant(value);
}
/// Set the field value from a QVariant
static void setFromVariant(const QVariant& variantValue, T& value)
{
value = variantValue.value<T>();
}
/// Methods to get a list of options for a field, specialized for AppEnum
static QList<PdmOptionItemInfo> valueOptions( bool* useOptionsOnly, const T& )
{
return QList<PdmOptionItemInfo>();
}
/// Methods to retrieve the possible PdmObject pointed to by a field
static void childObjects(const PdmField<T>& , std::vector<PdmObject*>* )
{ }
/*
static void writeFieldData(PdmField<T> & field, QXmlStreamWriter& xmlStream)
{
QString dataString;
QTextStream data(&dataString, QIODevice::WriteOnly);
data << field.v();
xmlStream.writeCharacters(dataString);
}
static void readFieldData(PdmField<DataType> & field, QXmlStreamReader& xmlStream)
{
PdmFieldHandle::skipComments(xmlStream);
if (!xmlStream.isCharacters()) return;
QString dataString = xmlStream.text().toString();
QTextStream data(&dataString, QIODevice::ReadOnly);
data >> field.v();
// Make stream point to end of element
QXmlStreamReader::TokenType type;
type = xmlStream.readNext();
PdmFieldHandle::skipCharactersAndComments(xmlStream);
}
*/
};
//==================================================================================================
/// Partial specialization for PdmField< std::list<T> >
//==================================================================================================
template <typename T>
class PdmFieldTypeSpecialization < std::list<T> >
{
public:
/// Convert the field value into a QVariant
static QVariant convert(const std::list<T>& value)
{
QList<QVariant> returnList;
typename std::list<T>::const_iterator it;
for (it = value.begin(); it != value.end() ; ++it)
{
returnList.push_back(QVariant(*it));
}
return returnList;
}
/// Set the field value from a QVariant
static void setFromVariant(const QVariant& variantValue, std::list<T>& value)
{
if (variantValue.canConvert< QList<QVariant> >())
{
value.clear();
QList<QVariant> lst = variantValue.toList();
int i;
for (i = 0; i < lst.size(); ++i)
{
value.push_back(lst[i].value<T>());
}
}
}
/// Methods to get a list of options for a field, specialized for AppEnum
static QList<PdmOptionItemInfo> valueOptions( bool* useOptionsOnly, const std::list<T>& )
{
return QList<PdmOptionItemInfo>();
}
/// Methods to retrieve the possible PdmObject pointed to by a field
static void childObjects(const PdmField< std::list<T> >& , std::vector<PdmObject*>* )
{ }
};
//==================================================================================================
/// Partial specialization for PdmField< std::list< PdmPointer<T> > >
//==================================================================================================
template <typename T>
class PdmFieldTypeSpecialization < std::list<PdmPointer<T> > >
{
public:
/// Convert the field value into a QVariant
static QVariant convert(const std::list<PdmPointer<T> >& )
{
return QVariant(); // Do nothing. The members are "children"
}
/// Set the field value from a QVariant
// Overloaded to do nothing because this is supposed to be handled as children
static void setFromVariant(const QVariant& , std::list<PdmPointer<T> >& )
{ }
/// Methods to get a list of options for a field, specialized for AppEnum
static QList<PdmOptionItemInfo> valueOptions( bool* useOptionsOnly, const std::list<PdmPointer<T> >& )
{
return QList<PdmOptionItemInfo>();
}
/// Methods to retrieve the possible PdmObject pointed to by a field
static void childObjects(const PdmField<std::list<PdmPointer<T> > >& field, std::vector<PdmObject*>* objects)
{
if (!objects) return;
typename std::list<PdmPointer<T> >::const_iterator it;
for (it = field.v().begin() ; it != field.v().end(); ++it)
{
objects->push_back(*it);
}
}
};
//==================================================================================================
/// Partial specialization for PdmField< std::vector<T> >
//==================================================================================================
template <typename T>
class PdmFieldTypeSpecialization < std::vector<T> >
{
public:
/// Convert the field value into a QVariant
static QVariant convert(const std::vector<T>& value)
{
QList<QVariant> returnList;
typename std::vector<T>::const_iterator it;
for (it = value.begin(); it != value.end() ; ++it)
{
returnList.push_back(QVariant(*it));
}
return returnList;
}
/// Set the field value from a QVariant
static void setFromVariant(const QVariant& variantValue, std::vector<T>& value)
{
if (variantValue.canConvert< QList<QVariant> >())
{
value.clear();
QList<QVariant> lst = variantValue.toList();
int i;
for (i = 0; i < lst.size(); ++i)
{
value.push_back(lst[i].value<T>());
}
}
}
/// Methods to get a list of options for a field, specialized for AppEnum
static QList<PdmOptionItemInfo> valueOptions( bool* useOptionsOnly, const std::vector<T>& )
{
return QList<PdmOptionItemInfo>();
}
/// Methods to retrieve the possible PdmObject pointed to by a field
static void childObjects(const PdmField< std::vector<T> > & field, std::vector<PdmObject*>* objects)
{ }
};
//==================================================================================================
/// Partial specialization for PdmField< caf::AppEnum<T> >
//==================================================================================================
template <typename T>
class PdmFieldTypeSpecialization < caf::AppEnum<T> >
{
public:
/// Convert the field value into a QVariant
static QVariant convert(const caf::AppEnum<T>& value)
{
return QVariant(static_cast<unsigned int>(caf::AppEnum<T>::index(value)));
}
/// Set the field value from a QVariant
static void setFromVariant(const QVariant& variantValue, caf::AppEnum<T>& value)
{
value.setFromIndex(variantValue.toInt());
}
/// Methods to get a list of options for a field, specialized for AppEnum
static QList<PdmOptionItemInfo> valueOptions( bool* useOptionsOnly, const caf::AppEnum<T>& )
{
if (useOptionsOnly) *useOptionsOnly = true;
QStringList optionTexts = caf::AppEnum<T>::uiTexts();
QList<PdmOptionItemInfo> optionList;
int i;
for (i = 0; i < optionTexts.size(); ++i)
{
optionList.push_back(PdmOptionItemInfo(optionTexts[i]));
}
return optionList;
}
/// Methods to retrieve the possible PdmObject pointed to by a field
static void childObjects(const PdmField< caf::AppEnum<T> >& field, std::vector<PdmObject*>* objects)
{ }
};
class PdmFieldIOHelper
{
public:
// Utility functions for reading from QXmlStreamReader
static void skipCharactersAndComments(QXmlStreamReader& xmlStream);
static void skipComments(QXmlStreamReader& xmlStream);
};
//--------------------------------------------------------------------------------------------------
/// Generic write method for fields. Will work as long as DataType supports the stream operator
/// towards a QTextStream. Some special datatype should not specialize this method unless it is
/// impossible/awkward to implement the stream operator
/// Implemented in a proxy class to allow partial specialization
//--------------------------------------------------------------------------------------------------
template <typename T>
struct PdmFieldWriter
{
static void writeFieldData(PdmField<T> & field, QXmlStreamWriter& xmlStream)
{
QString dataString;
QTextStream data(&dataString, QIODevice::WriteOnly);
data << field.v();
xmlStream.writeCharacters(dataString);
}
};
template <typename T>
struct PdmFieldReader
{
static void readFieldData(PdmField<T> & field, QXmlStreamReader& xmlStream);
};
//--------------------------------------------------------------------------------------------------
/// Generic read method for fields. Will work as long as DataType supports the stream operator
/// towards a QTextStream. Some special datatype should not specialize this method unless it is
/// impossible/awkward to implement the stream operator
//--------------------------------------------------------------------------------------------------
template<typename DataType >
void PdmFieldReader<DataType>::readFieldData(PdmField<DataType> & field, QXmlStreamReader& xmlStream)
{
PdmFieldIOHelper::skipComments(xmlStream);
if (!xmlStream.isCharacters()) return;
QString dataString = xmlStream.text().toString();
QTextStream data(&dataString, QIODevice::ReadOnly);
data >> field.v();
// Make stream point to end of element
QXmlStreamReader::TokenType type;
type = xmlStream.readNext();
PdmFieldIOHelper::skipCharactersAndComments(xmlStream);
}
//--------------------------------------------------------------------------------------------------
/// Specialized read function for QStrings, because the >> operator only can read word by word
//--------------------------------------------------------------------------------------------------
template<>
void PdmFieldReader<QString>::readFieldData(PdmField<QString> & field, QXmlStreamReader& xmlStream);
//==================================================================================================
/// Read and write method specializations for containers of pointers
/// std::list is the main one
//==================================================================================================
template <typename T>
struct PdmFieldWriter<std::list< PdmPointer<T> > >
{
static void writeFieldData(PdmField<std::list< PdmPointer<T> > > & field, QXmlStreamWriter& xmlStream)
{
typename std::list< PdmPointer<T> >::iterator it;
for (it = field.v().begin(); it != field.v().end(); ++it)
{
if (*it == NULL) continue;
QString className = (*it)->classKeyword();
xmlStream.writeStartElement("", className);
(*it)->writeFields(xmlStream);
xmlStream.writeEndElement();
}
}
};
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
template <typename T>
struct PdmFieldReader<std::list< PdmPointer<T> > >
{
static void readFieldData(PdmField<std::list< PdmPointer<T> > > & field, QXmlStreamReader& xmlStream)
{
T * currentObject = NULL;
PdmFieldIOHelper::skipCharactersAndComments(xmlStream);
while (xmlStream.isStartElement())
{
QString className = xmlStream.name().toString();
PdmObject * obj = PdmObjectFactory::instance()->create(className);
if (obj == NULL)
{
// Warning: Unknown className read
// Skip to corresponding end element
xmlStream.skipCurrentElement();
PdmFieldIOHelper::skipCharactersAndComments(xmlStream);
continue;
}
currentObject = dynamic_cast<T *> (obj);
if (currentObject == NULL)
{
// Warning: Inconsistency in factory !! Assert ?
// Skip to corresponding end element
xmlStream.skipCurrentElement();
PdmFieldIOHelper::skipCharactersAndComments(xmlStream);
continue;
}
currentObject->readFields(xmlStream);
field.v().push_back(currentObject);
// Skip comments and for some reason: Characters. The last bit should not be correct,
// but Qt reports a character token between EndElement and StartElement
QXmlStreamReader::TokenType type;
type = xmlStream.readNext();
PdmFieldIOHelper::skipCharactersAndComments(xmlStream);
}
}
};
} // End of namespace caf
//==================================================================================================
/// QTextStream Stream operator overloading for std::vector of things.
/// Makes automated IO of PdmField< std::vector< Whatever > possible as long as
/// the type will print as one single word
//==================================================================================================
template < typename T >
void operator << (QTextStream& str, const std::vector<T>& sobj)
{
size_t i;
for (i = 0; i < sobj.size(); ++i)
{
str << sobj[i] << " ";
}
}
template < typename T >
void operator >> (QTextStream& str, std::vector<T>& sobj)
{
while (str.status() == QTextStream::Ok )
{
T d;
str >> d;
if (str.status() == QTextStream::Ok ) sobj.push_back(d);
}
}
//==================================================================================================
/// QTextStream Stream operator overloading for bool`s
/// Prints bool`s as "True"/"False", and reads them too
//==================================================================================================
void operator >> (QTextStream& str, bool& value);
void operator << (QTextStream& str, const bool& value);