From: Ivan B. <iv...@cv...> - 2010-08-01 13:47:48
|
Hi all, I've just finished skeleton of changes in toQValue. The main purpose is to hold and display(not necessary edit) some non-trivial database datatypes(LOBs, XMLTYPE, IP addresses, ...) So far the class toQValue is wrapper around QT's QVariant, not it's subclass. It has quite huge API, but we use a small fraction of it. This class is used for two "different" purposes - either for data read from a query or for list of query's arguments. I added a nested abstract class complexType inside toQValue. This class has not data fields and represents an interface for non-trivial types. This class is: - registered with QT's meta-type system and can be stored inside QVariant. In fact it is assumed that QVariant holds a pointer to some subclass of complexType whenever Value.type() == QVariant.UserType. - to be subclassed in a connection provider. - used in toResultModel and toMemoEditor. Virtual methods implemented in a connection provider are called from toModelEditor I do not want to commit this into trunk before we release a new version. This change touches internal structures and needs to be tested first. I still have problems with cloning the class toQValue, I was quite surprised how many times we copy one toQValue into another before we display it. Problem occurs when I clone toQValue and then delete either an original or a copy. The destructor deletes an instance of complexType and the other instance points to newhere. Please send me comments to this change. This is my first bigger change of Tora's internals. Ivan #@ Add nested class complexType #@ method toQVariant() now returns const&, not a QVariant's copy #@ complexType registered type Index: toqvalue.h =================================================================== --- toqvalue.h (revision 3644) +++ toqvalue.h (working copy) @@ -60,6 +60,23 @@ QVariant Value; public: + /** + * This is helper class for visualization of complex types + * like: CLOB, BLOB, XML, vector of strings, image... + * This class should be subclassed in tooracleconnection and other + * connection providers. Subclasses then will be registered + * into QT's metatype system using Q_DECLARE_METATYPE and stored inside QVariant + */ + class complexType + { + public: + virtual bool isBinary() const = 0; + virtual bool isLarge() const = 0; + virtual QString summary() const = 0; + virtual QString cellData() const = 0; + virtual ~complexType() {}; + }; + /** Create null value. */ toQValue(void); @@ -133,7 +150,10 @@ /** Check if this value is binary. */ bool isBinary(void) const; - + /** Check if this value holds "custom" user type + */ + bool isUserType(void) const; + /** Get toUtf8 format of this value. */ QString toUtf8(void) const; @@ -160,7 +180,7 @@ /** Convert value to a QVariant */ - QVariant toQVariant(void) const; + QVariant const& toQVariant(void) const; /** Get binary representation of value. Can only be called when the data is actually binary. */ @@ -204,6 +224,7 @@ */ static toQValue fromVariant(const QVariant &); }; +Q_DECLARE_METATYPE(toQValue::complexType*) /** A short representation of list<toQuery::queryValue> */ #@ toQValue clonning achieved by using several methods by copy-construktor or by assignment operator. I added a destructive behavior into these methods as I can not simply clone LOB locators. Only one copy of locator can be help on one time. #@ Please ignore returned strings "[T|O] UserType". These are debug outputs only and I think that these methods are never called Index: toqvalue.cpp =================================================================== --- toqvalue.cpp (revision 3644) +++ toqvalue.cpp (working copy) @@ -76,8 +76,28 @@ toQValue::toQValue(const toQValue ©) { Value = copy.Value; + /** Be destructive only if complexType is held + * There should be no copying of data read from a query, + * but toQValue is also used for query parameters(toQList and others) + * and these are copyined often (toNoBlockQuery.Params => toQuery.Params) + */ + if(isUserType()) + const_cast<toQValue&>(copy).Value = "deleted value(clone)"; } +const toQValue &toQValue::operator = (const toQValue & copy) +{ + Value = copy.Value; + /** Be destructive only if complexType is held + * There should be no copying of data read from a query, + * but toQValue is also used for query parameters(toQList and others) + * and these are copyined often (toNoBlockQuery.Params => toQuery.Params) + */ + if(isUserType()) + const_cast<toQValue&>(copy).Value = "deleted value(assign)"; + return *this; +} + toQValue::toQValue(const QString &str) { Value = str; @@ -89,6 +109,12 @@ toQValue::~toQValue() { + if(isUserType()) + { + complexType *i = Value.value<toQValue::complexType*>(); + if(i) + delete i; + } } bool toQValue::operator<(const toQValue &other) const @@ -156,13 +182,6 @@ return !operator<=(other); } - -const toQValue &toQValue::operator = (const toQValue & copy) -{ - Value = copy.Value; - return *this; -} - bool toQValue::isNumber() const { return isInt() || isDouble() || isLong() || isuLong(); @@ -173,7 +192,7 @@ return Value == val.Value; } -QVariant toQValue::toQVariant() const +QVariant const& toQValue::toQVariant() const { return Value; } @@ -208,6 +227,11 @@ return Value.type() == QVariant::ByteArray; } +bool toQValue::isUserType(void) const +{ + return Value.type() == QVariant::UserType; +} + bool toQValue::isNull() const { return Value.isNull(); @@ -220,12 +244,18 @@ QString toQValue::toUtf8() const { - return Value.toString(); + if(isUserType()) + return QString("U UserType"); + else + return Value.toString(); } QString toQValue::toString() const { - return Value.toString(); + if(isUserType()) + return QString("T UserType"); + else + return Value.toString(); } int toQValue::toInt() const @@ -358,7 +388,10 @@ toQValue::operator QString() const { - return Value.toString(); + if(isUserType()) + return QString("O UserType"); + else + return Value.toString(); } @@ -393,6 +426,9 @@ case 4: s.append("T"); break; + case 5: + s.append("P"); + break; default: s.append("E"); } #@ these classes are used only for displaying the string "{CLOB}, I think I'll delete them and implement them same behavior through complexType's interface. Index: utils.h =================================================================== --- utils.h (revision 3644) +++ utils.h (working copy) @@ -364,6 +364,12 @@ /** Convert string read by readValueNull to value to be read by readValue. */ toQValue toNull(const toQValue &str); +/** ... TODO Comment me + */ +toQValue toCLOB(const toQValue &str); +/** .. TODO Comment me + */ +toQValue toBLOB(const toQValue &str); /** Take a string and make it illegible. Some security through obscurity here so you will * need to check the source to see what is actually done. #@ these classes are used only for displaying the string "{CLOB}, I think I'll delete them and implement them same behavior through complexType's interface. Index: utils.cpp =================================================================== --- utils.cpp (revision 3644) +++ utils.cpp (working copy) @@ -1173,6 +1173,16 @@ return str; } +toQValue toCLOB(const toQValue &str) +{ + return QString::fromLatin1("{CLOB}"); +} + +toQValue toBLOB(const toQValue &str) +{ + return QString::fromLatin1("{BLOB}"); +} + QString toTranslateMayby(const QString &ctx, const QString &text) { if (ctx.contains(QString::fromLatin1(" ")) || ctx.toLatin1() != ctx.toUtf8() || text.toLatin1() != text.toUtf8() || ctx.isEmpty() || text.isEmpty()) #@ Call complexType::summary() and display this output in MemoEditor Index: tomodeleditor.cpp =================================================================== --- tomodeleditor.cpp (revision 3644) +++ tomodeleditor.cpp (working copy) @@ -332,10 +332,20 @@ void toModelEditor::changePosition(QModelIndex index) { Current = index; + QVariant const &data = Model->data(Current, Qt::UserRole); + if(data.type() == QVariant::UserType) + { + toQValue::complexType *i = data.value<toQValue::complexType*>(); + setText(i->summary()); + return; + } + if(Editable) - setText(Model->data(Current, Qt::EditRole).toString()); + setText(Model->data(Current, Qt::EditRole).toString()); else - setText(Model->data(Current, Qt::DisplayRole).toString()); + { + setText(Model->data(Current, Qt::DisplayRole).toString()); + } } void toModelEditor::firstColumn() #@ avoid unnecesary copying of toQValue #@ Use QT::UserRole to retreive pointer to complexType Index: toresultmodel.cpp =================================================================== --- toresultmodel.cpp (revision 3644) +++ toresultmodel.cpp (working copy) @@ -494,13 +494,14 @@ if (index.row() > Rows.size() - 1 || index.column() > Headers.size() - 1) return QVariant(); - toQValue data = Rows.at(index.row()).at(index.column()); + toQValue const &data = Rows.at(index.row()).at(index.column()); if (role == Qt::DisplayRole) { if (data.isNull()) return toNull(data).toQVariant(); - + if (data.isUserType()) + return toCLOB(data).toQVariant(); return data.toQVariant(); } if (role == Qt::EditRole) @@ -515,7 +516,10 @@ } if (role == Qt::TextAlignmentRole) return (int) Headers.at(index.column()).align; - + if (role == Qt::UserRole) + { + return data.toQVariant(); + } return QVariant(); } #@ Subclass complexType. The methods are not implemented yet #@ output from method summary is displayed in memo editor for CLOB datatype Index: tooracleconnection.cpp =================================================================== --- tooracleconnection.cpp (revision 3644) +++ tooracleconnection.cpp (working copy) @@ -51,6 +51,7 @@ #endif #include <trotl.h> +#include <trotl_convertor.h> #include "toconf.h" #include "toconfiguration.h" @@ -153,6 +154,49 @@ } } +class toOracleClob: public toQValue::complexType +{ +public: + toOracleClob(trotl::OciConnection &_conn) + : toQValue::complexType(), + data(_conn) + {}; + /* virtual */ bool isBinary() const + { + return false; + } + /* virtual */ bool isLarge() const + { + return false; + } + /* virtual */ QString summary() const + { + return QString("XXX"); + } + /* virtual */ QString cellData() const + { + return QString("XXX"); + } + ~toOracleClob() + { + TLOG(1,toDecorator,__HERE__) << "toOracleClob DELETED:" << this << std::endl; + } + + trotl::SqlClob data; +protected: + toOracleClob(toOracleClob const&); + toOracleClob operator=(toOracleClob const&); + //TODO copying prohibited +}; +//Q_DECLARE_METATYPE(toOracleClob*) + +class toOracleBlob: public ::trotl::SqlBlob, toQValue::complexType +{ + toOracleBlob(trotl::OciConnection &_conn) : trotl::SqlBlob(_conn), toQValue::complexType() + {}; +}; +//Q_DECLARE_METATYPE(toOracleBlob*) + class toOracleProvider : public toConnectionProvider { ::trotl::OciEnv *_envp; @@ -332,7 +376,21 @@ break; #endif case SQLT_CLOB: { - value = toQValue(QString("<CLOB>")); + //trotl::SqlClob *i = new ::trotl::SqlClob(_conn); + toOracleClob *i = new toOracleClob(_conn); + trotl::ConvertorForRead c(_last_buff_row); + trotl::DispatcherForRead::Go(BP, i->data, c); + QVariant v; + v.setValue((toQValue::complexType*)i); + //value = toQValue(QString("<CLOB>")); + value = toQValue::fromVariant(v); + //int id = qMetaTypeId<toOracleClob*>(); + int id = qMetaTypeId<toQValue::complexType*>(); + TLOG(0,toDecorator,__HERE__) << "trotl::SqlClob* datatype registered as: '" << id << "' " + << "toQValue::isUserType(): '" << value.isUserType() << "' " + << "toQValue::toString(): '" << value.toString() << "' " + << "toQValue::operator QString()(): '" << (QString)value << "'" + << std::endl; } break; case SQLT_BLOB: { |