File indexing completed on 2025-03-23 03:32:46

0001 // xlsxcellformula.cpp
0002 
0003 #include <QtGlobal>
0004 #include <QObject>
0005 #include <QString>
0006 #include <QXmlStreamReader>
0007 #include <QXmlStreamWriter>
0008 #include <QDebug>
0009 
0010 #include "xlsxcellformula.h"
0011 #include "xlsxcellformula_p.h"
0012 #include "xlsxutility_p.h"
0013 
0014 QT_BEGIN_NAMESPACE_XLSX
0015 
0016 CellFormulaPrivate::CellFormulaPrivate(const QString &formula_, const CellRange &ref_, CellFormula::FormulaType type_)
0017     :formula(formula_), type(type_), reference(ref_), ca(false), si(0)
0018 {
0019     //Remove the formula '=' sign if exists
0020     if (formula.startsWith(QLatin1String("=")))
0021         formula.remove(0,1);
0022     else if (formula.startsWith(QLatin1String("{=")) && formula.endsWith(QLatin1String("}")))
0023         formula = formula.mid(2, formula.length()-3);
0024 }
0025 
0026 CellFormulaPrivate::CellFormulaPrivate(const CellFormulaPrivate &other)
0027     : QSharedData(other)
0028     , formula(other.formula), type(other.type), reference(other.reference)
0029     , ca(other.ca), si(other.si)
0030 {
0031 
0032 }
0033 
0034 CellFormulaPrivate::~CellFormulaPrivate()
0035 {
0036 
0037 }
0038 
0039 /*!
0040   \class CellFormula
0041   \inmodule QtXlsx
0042   \brief The CellFormula class provides a API that is used to handle the cell formula.
0043 
0044 */
0045 
0046 /*!
0047   \enum CellFormula::FormulaType
0048   \value NormalType
0049   \value ArrayType
0050   \value DataTableType
0051   \value SharedType
0052 */
0053 
0054 /*!
0055  *  Creates a new formula.
0056  */
0057 CellFormula::CellFormula()
0058 {
0059     //The d pointer is initialized with a null pointer
0060 }
0061 
0062 /*!
0063  *  Creates a new formula with the given \a formula and \a type.
0064  */
0065 CellFormula::CellFormula(const char *formula, FormulaType type)
0066     :d(new CellFormulaPrivate(QString::fromLatin1(formula), CellRange(), type))
0067 {
0068 
0069 }
0070 
0071 /*!
0072  *  Creates a new formula with the given \a formula and \a type.
0073  */
0074 CellFormula::CellFormula(const QString &formula, FormulaType type)
0075     :d(new CellFormulaPrivate(formula, CellRange(), type))
0076 {
0077 
0078 }
0079 
0080 /*!
0081  *  Creates a new formula with the given \a formula, \a ref and \a type.
0082  */
0083 CellFormula::CellFormula(const QString &formula, const CellRange &ref, FormulaType type)
0084     :d(new CellFormulaPrivate(formula, ref, type))
0085 {
0086 
0087 }
0088 
0089 /*!
0090    Creates a new formula with the same attributes as the \a other formula.
0091  */
0092 CellFormula::CellFormula(const CellFormula &other)
0093     :d(other.d)
0094 {
0095 }
0096 
0097 /*!
0098    Assigns the \a other formula to this formula, and returns a
0099    reference to this formula.
0100  */
0101 CellFormula &CellFormula::operator =(const CellFormula &other)
0102 {
0103     d = other.d;
0104     return *this;
0105 }
0106 
0107 /*!
0108  * Destroys this formula.
0109  */
0110 CellFormula::~CellFormula()
0111 {
0112 
0113 }
0114 
0115 /*!
0116  * Returns the type of the formula.
0117  */
0118 CellFormula::FormulaType CellFormula::formulaType() const
0119 {
0120     return d ? d->type : NormalType;
0121 }
0122 
0123 /*!
0124  * Returns the contents of the formula.
0125  */
0126 QString CellFormula::formulaText() const
0127 {
0128     return d ? d->formula : QString();
0129 }
0130 
0131 /*!
0132  * Returns the reference cells of the formula. For normal formula,
0133  * this will return an invalid CellRange object.
0134  */
0135 CellRange CellFormula::reference() const
0136 {
0137     return d ? d->reference : CellRange();
0138 }
0139 
0140 /*!
0141  * Returns whether the formula is valid.
0142  */
0143 bool CellFormula::isValid() const
0144 {
0145     return d;
0146 }
0147 
0148 /*!
0149  * Returns the shared index for shared formula.
0150  */
0151 int CellFormula::sharedIndex() const
0152 {
0153     return d && d->type == SharedType ? d->si : (-1);
0154 }
0155 
0156 /* aca (Always Calculate Array) // not-implmented attribute
0157  *
0158  * Only applies to array formulas.
0159  *
0160  * true indicates that the entire array shall be calculated in full.
0161  * If false the individual cells of the array shall be calculated as needed.
0162  *
0163  * The aca value shall be ignored unless the value of the corresponding
0164  *  t attribute is array.
0165  *
0166  *  [Note: The primary case where an array formula must be calculated in
0167  * part instead of in full is when some cells in the array depend on other
0168  * cells that are semi-calculated, e.g., contains the function =(). end note]
0169  *
0170  *  The possible values for this attribute are defined by the W3C XML Schema
0171  *  boolean datatype.
0172  */
0173 
0174 /* bx (Assigns Value to Name) // not-implmented attribute
0175  *
0176  * Specifies that this formula assigns a value to a name.
0177  *
0178  * The possible values for this attribute are defined by the W3C XML
0179  * Schema boolean datatype.
0180  */
0181 
0182 /* del1 (Input 1 Deleted) // not-implmented attribute
0183  *
0184  * Whether the first input cell for data table has been deleted.
0185  * Applies to data table formula only. Written on master cell of data table
0186  * formula only.
0187  *
0188  * The possible values for this attribute are defined by the W3C XML Schema
0189  * boolean datatype.
0190 */
0191 
0192 /* del2 (Input 2 Deleted) // not-impplmented attribute
0193  *
0194  * Whether the second input cell for data table has been deleted.
0195  * Applies to data table formula only. Written on master cell of data
0196  * table formula only.
0197  *
0198  * The possible values for this attribute are defined by the W3C XML Schema
0199  * boolean datatype.
0200  */
0201 
0202 /* dt2D (Data Table 2-D) // not-implmented attribute
0203  *
0204  * Data table is two-dimentional. Only applies to the data tables function.
0205  * Written on master cell of data table formula only.
0206  *
0207  * The possible values for this attribute are defined by the W3C XML Schema
0208  * boolean datatype.
0209  */
0210 
0211 /* dtr (Data Table Row) // not-implmented attribute
0212  *
0213  * true if one-dimentional data table is a row, otherwise it's a column.
0214  * Only applies to the data tables function. Written on master cell of data
0215  * table formula only.
0216  *
0217  * The possible values for this attribute are defined by the W3C XML Schema
0218  *  boolean datatype.
0219  */
0220 
0221 /* r1 (Data Table Cell 1) // not-implmented attribute
0222  *
0223  * First input cell for data table. Only applies to the data tables array
0224  * function "TABLE()". Written on master cell of data table formula only.
0225  *
0226  * The possible values for this attribute are defined by the ST_CellRef
0227  * simple type (§18.18.7).
0228  */
0229 
0230 /* r2 (Input Cell 2) // not-implmented attribute
0231  *
0232  * Second input cell for data table when dt2D is '1'. Only applies to the
0233  * data tables array function "TABLE()".Written on master cell of data table
0234  * formula only.
0235  *
0236  * The possible values for this attribute are defined by the ST_CellRef
0237  * simple type (§18.18.7).
0238  */
0239 
0240 /*!
0241  * \internal
0242  * \remark pair with loadFromXml()
0243  */
0244 bool CellFormula::saveToXml(QXmlStreamWriter &writer) const
0245 {
0246 
0247     // t (Formula Type)
0248     //
0249     // Type of formula.
0250     // The possible values for this attribute are defined by the
0251     // ST_CellFormulaType simple type (§18.18.6).
0252     //
0253     // 18.18.6 ST_CellFormulaType (Formula Type)
0254     // array (Array Formula)
0255     // dataTable (Table Formula)
0256     // normal (Normal)
0257     // shared (Shared Formula)
0258 
0259     QString t;
0260     switch (d->type)
0261     {
0262     case CellFormula::ArrayType:
0263         t = QStringLiteral("array");
0264         break;
0265     case CellFormula::SharedType:
0266         t = QStringLiteral("shared");
0267         break;
0268     case CellFormula::NormalType:
0269         t = QStringLiteral("normal");
0270         break;
0271     case CellFormula::DataTableType:
0272         t = QStringLiteral("dataTable");
0273         break;
0274     default: // undefined type
0275         return false;
0276         break;
0277     }
0278 
0279     // f (Formula)
0280     //
0281     // Formula for the cell. The formula expression is contained in the
0282     // character node of this element.
0283     writer.writeStartElement(QStringLiteral("f"));
0284 
0285     if (!t.isEmpty())
0286     {
0287         writer.writeAttribute(QStringLiteral("t"), t); // write type(t)
0288     }
0289 
0290     // ref (Range of Cells)
0291     //
0292     // Range of cells which the formula applies to.
0293     // Only required for shared formula, array formula or data table.
0294     // Only written on the master formula,
0295     // not subsequent formulas belonging to the same shared group, array,
0296     // or data table.
0297     // The possible values for this attribute are defined by the ST_Ref
0298     // simple type (§18.18.62).
0299 
0300     if ( d->type == CellFormula::SharedType ||
0301          d->type == CellFormula::ArrayType ||
0302          d->type == CellFormula::DataTableType )
0303     {
0304         if (d->reference.isValid())
0305         {
0306             writer.writeAttribute(QStringLiteral("ref"), d->reference.toString());
0307         }
0308     }
0309 
0310     // ca (Calculate Cell)
0311     //
0312     // Indicates that this formula needs to be recalculated the next time
0313     // calculation is performed. [Example: This is always set on volatile
0314     // functions, like =(), and circular references. end example]
0315     // The possible values for this attribute are defined by the W3C XML
0316     // Schema boolean datatype.
0317     //
0318     // 3.2.2 boolean
0319     // 3.2.2.1 Lexical representation
0320     // An instance of a datatype that is defined as ·boolean· can have the
0321     // following legal literals {true, false, 1, 0}.
0322 
0323     if (d->ca)
0324     {
0325         writer.writeAttribute(QStringLiteral("ca"), QStringLiteral("1"));
0326     }
0327 
0328     // si (Shared Group Index)
0329     // Optional attribute to optimize load performance by sharing formulas.
0330     //
0331     // When a formula is a shared formula (t value is shared) then this value
0332     // indicates the group to which this particular cell's formula belongs. The
0333     // first formula in a group of shared formulas is saved in the f element.
0334     // This is considered the 'master' formula cell. Subsequent cells sharing
0335     // this formula need not have the formula written in their f element.
0336     // Instead, the attribute si value for a particular cell is used to figure
0337     // what the formula expression should be based on the cell's relative
0338     // location to the master formula cell.
0339 
0340     if (d->type == CellFormula::SharedType)
0341     {
0342         int si = d->si;
0343         writer.writeAttribute(QStringLiteral("si"), QString::number(si));
0344     }
0345 
0346     if (!d->formula.isEmpty())
0347     {
0348         QString strFormula = d->formula;
0349         writer.writeCharacters(strFormula); // write formula
0350     }
0351 
0352     writer.writeEndElement(); // f
0353 
0354     return true;
0355 }
0356 
0357 /*!
0358  * \internal
0359  * \remark pair with saveToXml()
0360  */
0361 bool CellFormula::loadFromXml(QXmlStreamReader &reader)
0362 {
0363     Q_ASSERT(reader.name() == QLatin1String("f"));
0364     if (!d)
0365         d = new CellFormulaPrivate(QString(), CellRange(), NormalType);
0366 
0367     QXmlStreamAttributes attributes = reader.attributes();
0368     QString typeString = attributes.value(QLatin1String("t")).toString();
0369 
0370     // branch: shared-formula
0371     //
0372     if (typeString == QLatin1String("array")) {
0373         d->type = ArrayType;
0374     }
0375     else if (typeString == QLatin1String("shared")) {
0376         d->type = SharedType;
0377     }
0378     else if (typeString == QLatin1String("normal")) {
0379         d->type = NormalType;
0380     }
0381     else if (typeString == QLatin1String("dataTable")) {
0382         d->type = DataTableType;
0383     }
0384     else {
0385         /*
0386         // undefined type
0387         // qDebug() << "Undefined type" << typeString;
0388         return false;
0389         // */
0390 
0391         // dev40 {{
0392         // https://github.com/QtExcel/QXlsx/issues/38
0393         d->type = NormalType; // Change: normal Type is not mentioned in the xml file!!!!!
0394         // }}
0395     }
0396 
0397     // branch: shared-formula
0398     //
0399     // ref (Range of Cells)
0400     // Range of cells which the formula applies to.
0401     // Only required for shared formula, array formula or data table.
0402     if ( d->type == CellFormula::SharedType ||
0403          d->type == CellFormula::ArrayType ||
0404          d->type == CellFormula::DataTableType )
0405     {
0406         if (attributes.hasAttribute(QLatin1String("ref")))
0407         {
0408             QString refString = attributes.value(QLatin1String("ref")).toString();
0409             d->reference = CellRange(refString);
0410         }
0411     }
0412 
0413     // branch: shared-formula
0414     //
0415     // si (Shared Group Index)
0416     // Optional attribute to optimize load performance by sharing formulas.
0417     // When a formula is a shared formula (t value is shared) then this value
0418     // indicates the group to which this particular cell's formula belongs.
0419     if ( d->type == CellFormula::SharedType )
0420     {
0421         QString ca = attributes.value(QLatin1String("si")).toString();
0422         d->ca = parseXsdBoolean(ca, false);
0423 
0424         if (attributes.hasAttribute(QLatin1String("si")))
0425         {
0426             d->si = attributes.value(QLatin1String("si")).toInt();
0427         }
0428     }
0429 
0430     d->formula = reader.readElementText(); // read formula
0431 
0432     return true;
0433 }
0434 
0435 /*!
0436  * \internal
0437  */
0438 bool CellFormula::operator ==(const CellFormula &formula) const
0439 {
0440     return d->formula == formula.d->formula && d->type == formula.d->type
0441             && d->si ==formula.d->si;
0442 }
0443 
0444 /*!
0445  * \internal
0446  */
0447 bool CellFormula::operator !=(const CellFormula &formula) const
0448 {
0449     return d->formula != formula.d->formula || d->type != formula.d->type
0450             || d->si !=formula.d->si;
0451 }
0452 
0453 QT_END_NAMESPACE_XLSX