Resolves: tdf#162093 TableRef item specifier may occur standalone
[LibreOffice.git] / sc / source / core / tool / formulalogger.cxx
blob79f1816a7fa508686a1338daf089822024996a99
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 */
8 #include <formulalogger.hxx>
9 #include <formulacell.hxx>
10 #include <tokenarray.hxx>
11 #include <document.hxx>
12 #include <docsh.hxx>
13 #include <tokenstringcontext.hxx>
14 #include <address.hxx>
15 #include <interpre.hxx>
17 #include <osl/file.hxx>
18 #include <sfx2/objsh.hxx>
19 #include <sfx2/docfile.hxx>
20 #include <tools/urlobj.hxx>
21 #include <formula/vectortoken.hxx>
22 #include <rtl/ustrbuf.hxx>
24 #include <cstdlib>
25 #include <utility>
27 namespace sc {
29 namespace {
31 std::unique_ptr<osl::File> initFile()
33 const char* pPath = std::getenv("LIBO_FORMULA_LOG_FILE");
34 if (!pPath)
35 return nullptr;
37 // Support both file:///... and system file path notations.
38 OUString aPath = OUString::createFromAscii(pPath);
39 INetURLObject aURL;
40 aURL.SetSmartURL(aPath);
41 aPath = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
43 return std::make_unique<osl::File>(aPath);
46 ScRefFlags getRefFlags( const ScAddress& rCellPos, const ScAddress& rRefPos )
48 ScRefFlags eFlags = ScRefFlags::VALID;
49 if (rCellPos.Tab() != rRefPos.Tab())
50 eFlags |= ScRefFlags::TAB_3D;
51 return eFlags;
56 FormulaLogger& FormulaLogger::get()
58 static FormulaLogger aLogger;
59 return aLogger;
62 struct FormulaLogger::GroupScope::Impl
64 FormulaLogger& mrLogger;
65 const ScDocument& mrDoc;
67 OUString maPrefix;
68 std::vector<OUString> maMessages;
70 bool mbCalcComplete;
71 bool mbOutputEnabled;
73 Impl( FormulaLogger& rLogger, OUString aPrefix, const ScDocument& rDoc,
74 const ScFormulaCell& rCell, bool bOutputEnabled ) :
75 mrLogger(rLogger), mrDoc(rDoc), maPrefix(std::move(aPrefix)),
76 mbCalcComplete(false), mbOutputEnabled(bOutputEnabled)
78 ++mrLogger.mnNestLevel;
80 if (!mbOutputEnabled)
81 return;
83 sc::TokenStringContext aCxt(rDoc, rDoc.GetGrammar());
84 OUString aFormula = rCell.GetCode()->CreateString(aCxt, rCell.aPos);
86 mrLogger.write(maPrefix);
87 mrLogger.writeNestLevel();
89 mrLogger.writeAscii("-- enter (formula='");
90 mrLogger.write(aFormula);
91 mrLogger.writeAscii("', size=");
92 mrLogger.write(rCell.GetSharedLength());
93 mrLogger.writeAscii(")\n");
96 ~Impl()
98 if (mbOutputEnabled)
100 for (const OUString& rMsg : maMessages)
102 mrLogger.write(maPrefix);
103 mrLogger.writeNestLevel();
104 mrLogger.writeAscii(" * ");
105 mrLogger.write(rMsg);
106 mrLogger.writeAscii("\n");
109 mrLogger.write(maPrefix);
110 mrLogger.writeNestLevel();
111 mrLogger.writeAscii("-- exit (");
112 if (mbCalcComplete)
113 mrLogger.writeAscii("calculation complete");
114 else
115 mrLogger.writeAscii("without calculation");
117 mrLogger.writeAscii(")\n");
119 mrLogger.sync();
122 --mrLogger.mnNestLevel;
126 FormulaLogger::GroupScope::GroupScope(
127 FormulaLogger& rLogger, const OUString& rPrefix, const ScDocument& rDoc,
128 const ScFormulaCell& rCell, bool bOutputEnabled ) :
129 mpImpl(std::make_unique<Impl>(rLogger, rPrefix, rDoc, rCell, bOutputEnabled)) {}
131 FormulaLogger::GroupScope::GroupScope(GroupScope&& r) noexcept : mpImpl(std::move(r.mpImpl)) {}
133 FormulaLogger::GroupScope::~GroupScope() {}
135 void FormulaLogger::GroupScope::addMessage( const OUString& rMsg )
137 mpImpl->maMessages.push_back(rMsg);
140 void FormulaLogger::GroupScope::addRefMessage(
141 const ScAddress& rCellPos, const ScAddress& rRefPos, size_t nLen,
142 const formula::VectorRefArray& rArray )
144 ScRange aRefRange(rRefPos);
145 aRefRange.aEnd.IncRow(nLen-1);
146 OUString aRangeStr = aRefRange.Format(mpImpl->mrDoc, getRefFlags(rCellPos, rRefPos));
148 std::u16string_view aMsg;
149 if (rArray.mpNumericArray)
151 if (rArray.mpStringArray)
153 // mixture of numeric and string cells.
154 aMsg = u"numeric and string";
156 else
158 // numeric cells only.
159 aMsg = u"numeric only";
162 else
164 if (rArray.mpStringArray)
166 // string cells only.
167 aMsg = u"string only";
169 else
171 // empty cells.
172 aMsg = u"empty";
176 mpImpl->maMessages.push_back(aRangeStr + ": " + aMsg);
179 void FormulaLogger::GroupScope::addRefMessage(
180 const ScAddress& rCellPos, const ScAddress& rRefPos, size_t nLen,
181 const std::vector<formula::VectorRefArray>& rArrays )
183 ScAddress aPos(rRefPos); // copy
184 for (const formula::VectorRefArray& rArray : rArrays)
186 addRefMessage(rCellPos, aPos, nLen, rArray);
187 aPos.IncCol();
191 void FormulaLogger::GroupScope::addRefMessage(
192 const ScAddress& rCellPos, const ScAddress& rRefPos,
193 const formula::FormulaToken& rToken )
195 OUString aPosStr = rRefPos.Format(getRefFlags(rCellPos, rRefPos), &mpImpl->mrDoc);
196 std::u16string_view aMsg;
197 switch (rToken.GetType())
199 case formula::svDouble:
200 aMsg = u"numeric value";
201 break;
202 case formula::svString:
203 aMsg = u"string value";
204 break;
205 default:
206 aMsg = u"unknown value";
209 mpImpl->maMessages.push_back(aPosStr + ": " + aMsg);
212 void FormulaLogger::GroupScope::addGroupSizeThresholdMessage( const ScFormulaCell& rCell )
214 OUString aBuf = "group length below minimum threshold ("
215 + OUString::number(rCell.GetWeight())
216 + " < "
217 + OUString::number(ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize)
218 + ")";
219 mpImpl->maMessages.push_back(aBuf);
222 void FormulaLogger::GroupScope::setCalcComplete()
224 mpImpl->mbCalcComplete = true;
225 addMessage(u"calculation performed"_ustr);
228 FormulaLogger::FormulaLogger()
230 mpLogFile = initFile();
232 if (!mpLogFile)
233 return;
235 osl::FileBase::RC eRC = mpLogFile->open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
237 if (eRC == osl::FileBase::E_EXIST)
239 eRC = mpLogFile->open(osl_File_OpenFlag_Write);
241 if (eRC != osl::FileBase::E_None)
243 // Failed to open an existing log file.
244 mpLogFile.reset();
245 return;
248 if (mpLogFile->setPos(osl_Pos_End, 0) != osl::FileBase::E_None)
250 // Failed to set the position to the end of the file.
251 mpLogFile.reset();
252 return;
255 else if (eRC != osl::FileBase::E_None)
257 // Failed to create a new file.
258 mpLogFile.reset();
259 return;
262 // Output the header information.
263 writeAscii("---\n");
264 writeAscii("OpenCL: ");
265 writeAscii(ScCalcConfig::isOpenCLEnabled() ? "enabled\n" : "disabled\n");
266 writeAscii("---\n");
268 sync();
271 FormulaLogger::~FormulaLogger()
273 if (mpLogFile)
274 mpLogFile->close();
277 void FormulaLogger::writeAscii( const char* s )
279 if (!mpLogFile)
280 return;
282 sal_uInt64 nBytes;
283 mpLogFile->write(s, strlen(s), nBytes);
286 void FormulaLogger::writeAscii( const char* s, size_t n )
288 if (!mpLogFile)
289 return;
291 sal_uInt64 nBytes;
292 mpLogFile->write(s, n, nBytes);
295 void FormulaLogger::write( std::u16string_view ou )
297 OString s = OUStringToOString(ou, RTL_TEXTENCODING_UTF8);
298 writeAscii(s.getStr(), s.getLength());
301 void FormulaLogger::write( sal_Int32 n )
303 OString s = OString::number(n);
304 writeAscii(s.getStr(), s.getLength());
307 void FormulaLogger::sync()
309 if (!mpLogFile)
310 return;
312 mpLogFile->sync();
315 void FormulaLogger::writeNestLevel()
317 // Write the nest level, but keep it only 1-character length to avoid
318 // messing up the spacing.
319 if (mnNestLevel < 10)
320 write(mnNestLevel);
321 else
322 writeAscii("!");
324 writeAscii(": ");
325 for (sal_Int32 i = 1; i < mnNestLevel; ++i)
326 writeAscii(" ");
329 FormulaLogger::GroupScope FormulaLogger::enterGroup(
330 const ScDocument& rDoc, const ScFormulaCell& rCell )
332 // Get the file name if available.
333 const ScDocShell* pShell = rDoc.GetDocumentShell();
334 const SfxMedium* pMedium = pShell ? pShell->GetMedium() : nullptr;
335 OUString aName;
336 if (pMedium)
337 aName = pMedium->GetURLObject().GetLastName();
338 if (aName.isEmpty())
339 aName = "-"; // unsaved document.
341 OUString aGroupPrefix = aName + ": formula-group: " +
342 rCell.aPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDoc, rDoc.GetAddressConvention()) + ": ";
344 bool bOutputEnabled = mpLastGroup != rCell.GetCellGroup().get();
345 mpLastGroup = rCell.GetCellGroup().get();
347 return GroupScope(*this, aGroupPrefix, rDoc, rCell, bOutputEnabled);
352 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */