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 }