File indexing completed on 2024-05-12 15:59:17

0001 /*
0002  *  SPDX-FileCopyrightText: 2007, 2010 Cyrille Berger <cberger@cberger.net>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.1-or-later
0005  */
0006 
0007 #include "kis_meta_data_value.h"
0008 #include <QPoint>
0009 #include <QPointF>
0010 #include <QRegExp>
0011 #include <QTime>
0012 #include <QVariant>
0013 #include <QStringList>
0014 
0015 #include <klocalizedstring.h>
0016 
0017 #include <kis_debug.h>
0018 
0019 using namespace KisMetaData;
0020 
0021 struct Q_DECL_HIDDEN Value::Private {
0022     Private() : type(Invalid) {}
0023     union {
0024         QVariant* variant;
0025         QList<Value>* array;
0026         QMap<QString, Value>* structure;
0027         KisMetaData::Rational* rational;
0028     } value;
0029     ValueType type;
0030     QMap<QString, Value> propertyQualifiers;
0031 };
0032 
0033 Value::Value() : d(new Private)
0034 {
0035     d->type = Invalid;
0036 }
0037 
0038 
0039 Value::Value(const QVariant& variant) : d(new Private)
0040 {
0041     d->type = Value::Variant;
0042     d->value.variant = new QVariant(variant);
0043 }
0044 
0045 Value::Value(const QList<Value>& array, ValueType type) : d(new Private)
0046 {
0047     Q_ASSERT(type == OrderedArray || type == UnorderedArray || type == AlternativeArray || type == LangArray);
0048     d->value.array = new QList<Value>(array);
0049     d->type = type; // TODO: I am hesitating about LangArray to keep them as array or convert them to maps
0050 }
0051 
0052 Value::Value(const QMap<QString, Value>& structure) : d(new Private)
0053 {
0054     d->type = Structure;
0055     d->value.structure = new QMap<QString, Value>(structure);
0056 }
0057 
0058 Value::Value(const KisMetaData::Rational& signedRational) : d(new Private)
0059 {
0060     d->type = Value::Rational;
0061     d->value.rational = new KisMetaData::Rational(signedRational);
0062 }
0063 
0064 
0065 Value::Value(const Value& v) : d(new Private)
0066 {
0067     d->type = Invalid;
0068     *this = v;
0069 }
0070 
0071 Value& Value::operator=(const Value & v)
0072 {
0073     d->type = v.d->type;
0074     d->propertyQualifiers = v.d->propertyQualifiers;
0075     switch (d->type) {
0076     case Invalid:
0077         break;
0078     case Variant:
0079         d->value.variant = new QVariant(*v.d->value.variant);
0080         break;
0081     case OrderedArray:
0082     case UnorderedArray:
0083     case AlternativeArray:
0084     case LangArray:
0085         d->value.array = new QList<Value>(*v.d->value.array);
0086         break;
0087     case Structure:
0088         d->value.structure = new QMap<QString, Value>(*v.d->value.structure);
0089         break;
0090     case Rational:
0091         d->value.rational = new KisMetaData::Rational(*v.d->value.rational);
0092     }
0093     return *this;
0094 }
0095 
0096 
0097 Value::~Value()
0098 {
0099     delete d;
0100 }
0101 
0102 void Value::addPropertyQualifier(const QString& _name, const Value& _value)
0103 {
0104     d->propertyQualifiers[_name] = _value;
0105 }
0106 
0107 const QMap<QString, Value>& Value::propertyQualifiers() const
0108 {
0109     return d->propertyQualifiers;
0110 }
0111 
0112 Value::ValueType Value::type() const
0113 {
0114     return d->type;
0115 }
0116 
0117 double Value::asDouble() const
0118 {
0119     switch (type()) {
0120     case Variant:
0121         return d->value.variant->toDouble(0);
0122     case Rational:
0123         return d->value.rational->numerator / (double)d->value.rational->denominator;
0124     default:
0125         return 0.0;
0126     }
0127     return 0.0;
0128 }
0129 
0130 int Value::asInteger() const
0131 {
0132     switch (type()) {
0133     case Variant:
0134         return d->value.variant->toInt(0);
0135     case Rational:
0136         return d->value.rational->numerator / d->value.rational->denominator;
0137     default:
0138         return 0;
0139     }
0140     return 0;
0141 }
0142 
0143 QVariant Value::asVariant() const
0144 {
0145     switch (type()) {
0146     case Variant:
0147         return *d->value.variant;
0148     case Rational:
0149         return QVariant(QString("%1 / %2").arg(d->value.rational->numerator).arg(d->value.rational->denominator));
0150     default: break;
0151     }
0152     return QVariant();
0153 }
0154 
0155 bool Value::setVariant(const QVariant& variant)
0156 {
0157     switch (type()) {
0158     case KisMetaData::Value::Invalid:
0159         *this = KisMetaData::Value(variant);
0160         return true;
0161     case Rational: {
0162         QRegExp rx("([^\\/]*)\\/([^\\/]*)");
0163         rx.indexIn(variant.toString());
0164         // TODO: erm... did someone forgot to write actual code here?
0165 
0166         // for now just safe assert and return a failure
0167         KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "Rational metadata values are not implemented!");
0168         return false;
0169     }
0170     case KisMetaData::Value::Variant: {
0171         if (d->value.variant->type() == variant.type()) {
0172             *d->value.variant = variant;
0173             return true;
0174         }
0175     }
0176     return true;
0177     default:
0178         break;
0179     }
0180     return false;
0181 }
0182 
0183 bool Value::setStructureVariant(const QString& fieldNAme, const QVariant& variant)
0184 {
0185     if (type() == Structure) {
0186         return (*d->value.structure)[fieldNAme].setVariant(variant);
0187     }
0188     return false;
0189 }
0190 
0191 bool Value::setArrayVariant(int index, const QVariant& variant)
0192 {
0193     if (isArray()) {
0194         for (int i = d->value.array->size(); i <= index; ++i) {
0195             d->value.array->append(Value());
0196         }
0197         (*d->value.array)[index].setVariant(variant);
0198     }
0199     return false;
0200 }
0201 
0202 KisMetaData::Rational Value::asRational() const
0203 {
0204     if (d->type == Rational) {
0205         return *d->value.rational;
0206     }
0207     return KisMetaData::Rational();
0208 }
0209 
0210 QList<Value> Value::asArray() const
0211 {
0212     if (isArray()) {
0213         return *d->value.array;
0214     }
0215     return QList<Value>();
0216 }
0217 
0218 
0219 bool Value::isArray() const
0220 {
0221     return type() == OrderedArray || type() == UnorderedArray || type() == AlternativeArray;
0222 }
0223 
0224 QMap<QString, KisMetaData::Value> Value::asStructure() const
0225 {
0226     if (type() == Structure) {
0227         return *d->value.structure;
0228     }
0229     return QMap<QString, KisMetaData::Value>();
0230 }
0231 
0232 QDebug operator<<(QDebug debug, const Value &v)
0233 {
0234     switch (v.type()) {
0235     case Value::Invalid:
0236         debug.nospace() << "invalid value";
0237         break;
0238     case Value::Variant:
0239         debug.nospace() << "Variant: " << v.asVariant();
0240         break;
0241     case Value::OrderedArray:
0242     case Value::UnorderedArray:
0243     case Value::AlternativeArray:
0244     case Value::LangArray:
0245         debug.nospace() << "Array: " << v.asArray();
0246         break;
0247     case Value::Structure:
0248         debug.nospace() << "Structure: " << v.asStructure();
0249         break;
0250     case Value::Rational:
0251         debug.nospace() << "Rational: " << v.asRational().numerator << " / " << v.asRational().denominator;
0252         break;
0253     }
0254     return debug.space();
0255 }
0256 
0257 bool Value::operator==(const Value& rhs) const
0258 {
0259     if (d->type != rhs.d->type) return false;
0260     switch (d->type) {
0261     case Value::Invalid:
0262         return true;
0263     case Value::Variant:
0264         return asVariant() == rhs.asVariant();
0265     case Value::OrderedArray:
0266     case Value::UnorderedArray:
0267     case Value::AlternativeArray:
0268     case Value::LangArray:
0269         return asArray() == rhs.asArray();
0270     case Value::Structure:
0271         return asStructure() == rhs.asStructure();
0272     case Value::Rational:
0273         return asRational() == rhs.asRational();
0274     }
0275     return false;
0276 }
0277 
0278 Value& Value::operator+=(const Value & v)
0279 {
0280     switch (d->type) {
0281     case Value::Invalid:
0282         Q_ASSERT(v.type() == Value::Invalid);
0283         break;
0284     case Value::Variant:
0285         Q_ASSERT(v.type() == Value::Variant);
0286         {
0287             QVariant v1 = *d->value.variant;
0288             QVariant v2 = *v.d->value.variant;
0289             Q_ASSERT(v2.canConvert(v1.type()));
0290             switch (v1.type()) {
0291             default:
0292                 warnMetaData << "KisMetaData: Merging metadata of type" << v1.type() << "is unsupported!";
0293                 break;
0294             case QVariant::Date:
0295                 *d->value.variant = qMax(v1.toDate(), v2.toDate());
0296                 break;
0297             case QVariant::DateTime:
0298                 *d->value.variant = qMax(v1.toDate(), v2.toDate());
0299                 break;
0300             case QVariant::Double:
0301                 *d->value.variant = v1.toDouble() + v2.toDouble();
0302                 break;
0303             case QVariant::Int:
0304                 *d->value.variant = v1.toInt() + v2.toInt();
0305                 break;
0306             case QVariant::List:
0307                 *d->value.variant = v1.toList() + v2.toList();
0308                 break;
0309             case QVariant::LongLong:
0310                 *d->value.variant = v1.toLongLong() + v2.toLongLong();
0311                 break;
0312             case QVariant::Point:
0313                 *d->value.variant = v1.toPoint() + v2.toPoint();
0314                 break;
0315             case QVariant::PointF:
0316                 *d->value.variant = v1.toPointF() + v2.toPointF();
0317                 break;
0318             case QVariant::String:
0319                 *d->value.variant = QVariant(v1.toString() + v2.toString());
0320                 break;
0321             case QVariant::StringList:
0322                 *d->value.variant = v1.toStringList() + v2.toStringList();
0323                 break;
0324             case QVariant::Time: {
0325                 QTime t1 = v1.toTime();
0326                 QTime t2 = v2.toTime();
0327                 int h = t1.hour() + t2.hour();
0328                 int m = t1.minute() + t2.minute();
0329                 int s = t1.second() + t2.second();
0330                 int ms = t1.msec() + t2.msec();
0331                 if (ms > 999) {
0332                     ms -= 999; s++;
0333                 }
0334                 if (s > 60) {
0335                     s -= 60; m++;
0336                 }
0337                 if (m > 60) {
0338                     m -= 60; h++;
0339                 }
0340                 if (h > 24) {
0341                     h -= 24;
0342                 }
0343                 *d->value.variant = QTime(h, m, s, ms);
0344             }
0345             break;
0346             case QVariant::UInt:
0347                 *d->value.variant = v1.toUInt() + v2.toUInt();
0348                 break;
0349             case QVariant::ULongLong:
0350                 *d->value.variant = v1.toULongLong() + v2.toULongLong();
0351                 break;
0352             }
0353 
0354         }
0355         break;
0356     case Value::OrderedArray:
0357     case Value::UnorderedArray:
0358     case Value::AlternativeArray: {
0359         if (v.isArray()) {
0360             *(d->value.array) += *(v.d->value.array);
0361         } else {
0362             d->value.array->append(v);
0363         }
0364     }
0365     break;
0366     case Value::LangArray: {
0367         Q_ASSERT(v.type() == Value::LangArray);
0368     }
0369     break;
0370     case Value::Structure: {
0371         Q_ASSERT(v.type() == Value::Structure);
0372         break;
0373     }
0374     case Value::Rational: {
0375         Q_ASSERT(v.type() == Value::Rational);
0376         d->value.rational->numerator =
0377             (d->value.rational->numerator
0378              * v.d->value.rational->denominator)
0379             + (v.d->value.rational->numerator
0380                * d->value.rational->denominator);
0381         d->value.rational->denominator *= v.d->value.rational->denominator;
0382         break;
0383     }
0384     }
0385     return *this;
0386 }
0387 
0388 QMap<QString, KisMetaData::Value> Value::asLangArray() const
0389 {
0390     Q_ASSERT(d->type == LangArray);
0391     QMap<QString, KisMetaData::Value> langArray;
0392     Q_FOREACH (const KisMetaData::Value& val, *d->value.array) {
0393         Q_ASSERT(val.d->propertyQualifiers.contains("xml:lang"));  // TODO probably worth to have an assert for this in the constructor as well
0394         KisMetaData::Value valKeyVal = val.d->propertyQualifiers.value("xml:lang");
0395         Q_ASSERT(valKeyVal.type() == Variant);
0396         QVariant valKeyVar = valKeyVal.asVariant();
0397         Q_ASSERT(valKeyVar.type() == QVariant::String);
0398         langArray[valKeyVar.toString()] = val;
0399     }
0400     return langArray;
0401 }
0402 
0403 QString Value::toString() const
0404 {
0405     switch (type()) {
0406     case Value::Invalid:
0407         return i18n("Invalid value.");
0408     case Value::Variant:
0409         return d->value.variant->toString();
0410         break;
0411     case Value::OrderedArray:
0412     case Value::UnorderedArray:
0413     case Value::AlternativeArray:
0414     case Value::LangArray: {
0415         QString r = QString("[%1]{ ").arg(d->value.array->size());
0416         for (int i = 0; i < d->value.array->size(); ++i) {
0417             const Value& val = d->value.array->at(i);
0418             r += val.toString();
0419             if (i != d->value.array->size() - 1) {
0420                 r += ',';
0421             }
0422             r += ' ';
0423         }
0424         r += '}';
0425         return r;
0426     }
0427     case Value::Structure: {
0428         QString r = "{ ";
0429         QList<QString> fields = d->value.structure->keys();
0430         for (int i = 0; i < fields.count(); ++i) {
0431             const QString& field = fields[i];
0432             const Value& val = d->value.structure->value(field);
0433             r += field + " => " + val.toString();
0434             if (i != d->value.array->size() - 1) {
0435                 r += ',';
0436             }
0437             r += ' ';
0438         }
0439         r += '}';
0440         return r;
0441     }
0442     break;
0443     case Value::Rational:
0444         return QString("%1 / %2").arg(d->value.rational->numerator).arg(d->value.rational->denominator);
0445     }
0446     return i18n("Invalid value.");
0447 }