qt/LedOK/QXlsx/source/xlsxcellformula.cpp

441 lines
12 KiB
C++
Raw Normal View History

2025-06-27 22:06:33 +08:00
// xlsxcellformula.cpp
#include "xlsxcellformula.h"
#include "xlsxcellformula_p.h"
#include "xlsxutility_p.h"
#include <QDebug>
#include <QObject>
#include <QString>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
QT_BEGIN_NAMESPACE_XLSX
CellFormulaPrivate::CellFormulaPrivate(const QString &formula_,
const CellRange &ref_,
CellFormula::FormulaType type_)
: formula(formula_)
, type(type_)
, reference(ref_)
, ca(false)
, si(0)
{
// Remove the formula '=' sign if exists
if (formula.startsWith(QLatin1String("=")))
formula.remove(0, 1);
else if (formula.startsWith(QLatin1String("{=")) && formula.endsWith(QLatin1String("}")))
formula = formula.mid(2, formula.length() - 3);
}
CellFormulaPrivate::CellFormulaPrivate(const CellFormulaPrivate &other)
: QSharedData(other)
, formula(other.formula)
, type(other.type)
, reference(other.reference)
, ca(other.ca)
, si(other.si)
{
}
CellFormulaPrivate::~CellFormulaPrivate()
{
}
/*!
\class CellFormula
\inmodule QtXlsx
\brief The CellFormula class provides a API that is used to handle the cell formula.
*/
/*!
\enum CellFormula::FormulaType
\value NormalType
\value ArrayType
\value DataTableType
\value SharedType
*/
/*!
* Creates a new formula.
*/
CellFormula::CellFormula()
{
// The d pointer is initialized with a null pointer
}
/*!
* Creates a new formula with the given \a formula and \a type.
*/
CellFormula::CellFormula(const char *formula, FormulaType type)
: d(new CellFormulaPrivate(QString::fromLatin1(formula), CellRange(), type))
{
}
/*!
* Creates a new formula with the given \a formula and \a type.
*/
CellFormula::CellFormula(const QString &formula, FormulaType type)
: d(new CellFormulaPrivate(formula, CellRange(), type))
{
}
/*!
* Creates a new formula with the given \a formula, \a ref and \a type.
*/
CellFormula::CellFormula(const QString &formula, const CellRange &ref, FormulaType type)
: d(new CellFormulaPrivate(formula, ref, type))
{
}
/*!
Creates a new formula with the same attributes as the \a other formula.
*/
CellFormula::CellFormula(const CellFormula &other)
: d(other.d)
{
}
/*!
Assigns the \a other formula to this formula, and returns a
reference to this formula.
*/
CellFormula &CellFormula::operator=(const CellFormula &other)
{
if (this != &other) // Self-assignment check [cert-oop54-cpp]
{
this->d = other.d;
}
return *this;
}
/*!
* Destroys this formula.
*/
CellFormula::~CellFormula()
{
}
/*!
* Returns the type of the formula.
*/
CellFormula::FormulaType CellFormula::formulaType() const
{
return d ? d->type : NormalType;
}
/*!
* Returns the contents of the formula.
*/
QString CellFormula::formulaText() const
{
return d ? d->formula : QString();
}
/*!
* Returns the reference cells of the formula. For normal formula,
* this will return an invalid CellRange object.
*/
CellRange CellFormula::reference() const
{
return d ? d->reference : CellRange();
}
/*!
* Returns whether the formula is valid.
*/
bool CellFormula::isValid() const
{
return d;
}
/*!
* Returns the shared index for shared formula.
*/
int CellFormula::sharedIndex() const
{
return d && d->type == SharedType ? d->si : (-1);
}
/* aca (Always Calculate Array) // not-implemented attribute
*
* Only applies to array formulas.
*
* true indicates that the entire array shall be calculated in full.
* If false the individual cells of the array shall be calculated as needed.
*
* The aca value shall be ignored unless the value of the corresponding
* t attribute is array.
*
* [Note: The primary case where an array formula must be calculated in
* part instead of in full is when some cells in the array depend on other
* cells that are semi-calculated, e.g., contains the function =(). end note]
*
* The possible values for this attribute are defined by the W3C XML Schema
* boolean datatype.
*/
/* bx (Assigns Value to Name) // not-implemented attribute
*
* Specifies that this formula assigns a value to a name.
*
* The possible values for this attribute are defined by the W3C XML
* Schema boolean datatype.
*/
/* del1 (Input 1 Deleted) // not-implemented attribute
*
* Whether the first input cell for data table has been deleted.
* Applies to data table formula only. Written on master cell of data table
* formula only.
*
* The possible values for this attribute are defined by the W3C XML Schema
* boolean datatype.
*/
/* del2 (Input 2 Deleted) // not-implemented attribute
*
* Whether the second input cell for data table has been deleted.
* Applies to data table formula only. Written on master cell of data
* table formula only.
*
* The possible values for this attribute are defined by the W3C XML Schema
* boolean datatype.
*/
/* dt2D (Data Table 2-D) // not-implemented attribute
*
* Data table is two-dimensional. Only applies to the data tables function.
* Written on master cell of data table formula only.
*
* The possible values for this attribute are defined by the W3C XML Schema
* boolean datatype.
*/
/* dtr (Data Table Row) // not-implemented attribute
*
* true if one-dimensional data table is a row, otherwise it's a column.
* Only applies to the data tables function. Written on master cell of data
* table formula only.
*
* The possible values for this attribute are defined by the W3C XML Schema
* boolean datatype.
*/
/* r1 (Data Table Cell 1) // not-implemented attribute
*
* First input cell for data table. Only applies to the data tables array
* function "TABLE()". Written on master cell of data table formula only.
*
* The possible values for this attribute are defined by the ST_CellRef
* simple type (§18.18.7).
*/
/* r2 (Input Cell 2) // not-implemented attribute
*
* Second input cell for data table when dt2D is '1'. Only applies to the
* data tables array function "TABLE()".Written on master cell of data table
* formula only.
*
* The possible values for this attribute are defined by the ST_CellRef
* simple type (§18.18.7).
*/
/*!
* \internal
* \remark pair with loadFromXml()
*/
bool CellFormula::saveToXml(QXmlStreamWriter &writer) const
{
// t (Formula Type)
//
// Type of formula.
// The possible values for this attribute are defined by the
// ST_CellFormulaType simple type (§18.18.6).
//
// 18.18.6 ST_CellFormulaType (Formula Type)
// array (Array Formula)
// dataTable (Table Formula)
// normal (Normal)
// shared (Shared Formula)
QString t;
switch (d->type) {
case CellFormula::ArrayType:
t = QStringLiteral("array");
break;
case CellFormula::SharedType:
t = QStringLiteral("shared");
break;
case CellFormula::NormalType:
t = QStringLiteral("normal");
break;
case CellFormula::DataTableType:
t = QStringLiteral("dataTable");
break;
default: // undefined type
return false;
break;
}
// f (Formula)
//
// Formula for the cell. The formula expression is contained in the
// character node of this element.
writer.writeStartElement(QStringLiteral("f"));
if (!t.isEmpty()) {
writer.writeAttribute(QStringLiteral("t"), t); // write type(t)
}
// ref (Range of Cells)
//
// Range of cells which the formula applies to.
// Only required for shared formula, array formula or data table.
// Only written on the master formula,
// not subsequent formulas belonging to the same shared group, array,
// or data table.
// The possible values for this attribute are defined by the ST_Ref
// simple type (§18.18.62).
if (d->type == CellFormula::SharedType || d->type == CellFormula::ArrayType ||
d->type == CellFormula::DataTableType) {
if (d->reference.isValid()) {
writer.writeAttribute(QStringLiteral("ref"), d->reference.toString());
}
}
// ca (Calculate Cell)
//
// Indicates that this formula needs to be recalculated the next time
// calculation is performed. [Example: This is always set on volatile
// functions, like =(), and circular references. end example]
// The possible values for this attribute are defined by the W3C XML
// Schema boolean datatype.
//
// 3.2.2 boolean
// 3.2.2.1 Lexical representation
// An instance of a datatype that is defined as ·boolean· can have the
// following legal literals {true, false, 1, 0}.
if (d->ca) {
writer.writeAttribute(QStringLiteral("ca"), QStringLiteral("1"));
}
// si (Shared Group Index)
// Optional attribute to optimize load performance by sharing formulas.
//
// When a formula is a shared formula (t value is shared) then this value
// indicates the group to which this particular cell's formula belongs. The
// first formula in a group of shared formulas is saved in the f element.
// This is considered the 'master' formula cell. Subsequent cells sharing
// this formula need not have the formula written in their f element.
// Instead, the attribute si value for a particular cell is used to figure
// what the formula expression should be based on the cell's relative
// location to the master formula cell.
if (d->type == CellFormula::SharedType) {
int si = d->si;
writer.writeAttribute(QStringLiteral("si"), QString::number(si));
}
if (!d->formula.isEmpty()) {
QString strFormula = d->formula;
writer.writeCharacters(strFormula); // write formula
}
writer.writeEndElement(); // f
return true;
}
/*!
* \internal
* \remark pair with saveToXml()
*/
bool CellFormula::loadFromXml(QXmlStreamReader &reader)
{
Q_ASSERT(reader.name() == QLatin1String("f"));
if (!d)
d = new CellFormulaPrivate(QString(), CellRange(), NormalType);
QXmlStreamAttributes attributes = reader.attributes();
QString typeString = attributes.value(QLatin1String("t")).toString();
// branch: shared-formula
//
if (typeString == QLatin1String("array")) {
d->type = ArrayType;
} else if (typeString == QLatin1String("shared")) {
d->type = SharedType;
} else if (typeString == QLatin1String("normal")) {
d->type = NormalType;
} else if (typeString == QLatin1String("dataTable")) {
d->type = DataTableType;
} else {
/*
// undefined type
// qDebug() << "Undefined type" << typeString;
return false;
// */
// dev40 {{
// https://github.com/QtExcel/QXlsx/issues/38
d->type = NormalType; // Change: normal Type is not mentioned in the xml file!!!!!
// }}
}
// branch: shared-formula
//
// ref (Range of Cells)
// Range of cells which the formula applies to.
// Only required for shared formula, array formula or data table.
if (d->type == CellFormula::SharedType || d->type == CellFormula::ArrayType ||
d->type == CellFormula::DataTableType) {
if (attributes.hasAttribute(QLatin1String("ref"))) {
QString refString = attributes.value(QLatin1String("ref")).toString();
d->reference = CellRange(refString);
}
}
// branch: shared-formula
//
// si (Shared Group Index)
// Optional attribute to optimize load performance by sharing formulas.
// When a formula is a shared formula (t value is shared) then this value
// indicates the group to which this particular cell's formula belongs.
if (d->type == CellFormula::SharedType) {
QString ca = attributes.value(QLatin1String("si")).toString();
d->ca = parseXsdBoolean(ca, false);
if (attributes.hasAttribute(QLatin1String("si"))) {
d->si = attributes.value(QLatin1String("si")).toInt();
}
}
d->formula = reader.readElementText(); // read formula
return true;
}
/*!
* \internal
*/
bool CellFormula::operator==(const CellFormula &formula) const
{
return d->formula == formula.d->formula && d->type == formula.d->type && d->si == formula.d->si;
}
/*!
* \internal
*/
bool CellFormula::operator!=(const CellFormula &formula) const
{
return d->formula != formula.d->formula || d->type != formula.d->type || d->si != formula.d->si;
}
QT_END_NAMESPACE_XLSX