xref: /core/sc/source/core/tool/calcconfig.cxx (revision 1d10db10)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <ostream>
11 #include <set>
12 
13 #include <formula/FormulaCompiler.hxx>
14 #include <formula/grammar.hxx>
15 #include <formula/opcode.hxx>
16 #include <rtl/ustring.hxx>
17 #include <sal/log.hxx>
18 #include <sfx2/objsh.hxx>
19 #include <unotools/configmgr.hxx>
20 
21 #include <calcconfig.hxx>
22 #include <compiler.hxx>
23 #include <docsh.hxx>
24 
25 #include <comphelper/configurationlistener.hxx>
26 #include <com/sun/star/datatransfer/XTransferable2.hpp>
27 
28 using comphelper::ConfigurationListener;
29 
30 static rtl::Reference<ConfigurationListener> const & getMiscListener()
31 {
32     static rtl::Reference<ConfigurationListener> xListener(new ConfigurationListener("/org.openoffice.Office.Common/Misc"));
33     return xListener;
34 }
35 
36 static rtl::Reference<ConfigurationListener> const & getFormulaCalculationListener()
37 {
38     static rtl::Reference<ConfigurationListener> xListener(new ConfigurationListener("/org.openoffice.Office.Calc/Formula/Calculation"));
39     return xListener;
40 }
41 
42 static ForceCalculationType forceCalculationTypeInit()
43 {
44     const char* env = getenv( "SC_FORCE_CALCULATION" );
45     if( env != nullptr )
46     {
47         if( strcmp( env, "opencl" ) == 0 )
48         {
49             SAL_INFO("sc.core.formulagroup", "Forcing calculations to use OpenCL");
50             return ForceCalculationOpenCL;
51         }
52         if( strcmp( env, "threads" ) == 0 )
53         {
54             SAL_INFO("sc.core.formulagroup", "Forcing calculations to use threads");
55             return ForceCalculationThreads;
56         }
57         if( strcmp( env, "core" ) == 0 )
58         {
59             SAL_INFO("sc.core.formulagroup", "Forcing calculations to use core");
60             return ForceCalculationCore;
61         }
62         SAL_WARN("sc.core.formulagroup", "Unrecognized value of SC_FORCE_CALCULATION");
63         abort();
64     }
65     return ForceCalculationNone;
66 }
67 
68 ForceCalculationType ScCalcConfig::getForceCalculationType()
69 {
70     static const ForceCalculationType type = forceCalculationTypeInit();
71     return type;
72 }
73 
74 bool ScCalcConfig::isOpenCLEnabled()
75 {
76     if (utl::ConfigManager::IsFuzzing())
77         return false;
78     static ForceCalculationType force = getForceCalculationType();
79     if( force != ForceCalculationNone )
80         return force == ForceCalculationOpenCL;
81     static comphelper::ConfigurationListenerProperty<bool> gOpenCLEnabled(getMiscListener(), "UseOpenCL");
82     return gOpenCLEnabled.get();
83 }
84 
85 bool ScCalcConfig::isThreadingEnabled()
86 {
87     if (utl::ConfigManager::IsFuzzing())
88         return false;
89     static ForceCalculationType force = getForceCalculationType();
90     if( force != ForceCalculationNone )
91         return force == ForceCalculationThreads;
92     static comphelper::ConfigurationListenerProperty<bool> gThreadingEnabled(getFormulaCalculationListener(), "UseThreadedCalculationForFormulaGroups");
93     return gThreadingEnabled.get();
94 }
95 
96 ScCalcConfig::ScCalcConfig() :
97     meStringRefAddressSyntax(formula::FormulaGrammar::CONV_UNSPECIFIED),
98     meStringConversion(StringConversion::LOCALE),     // old LibreOffice behavior
99     mbEmptyStringAsZero(false),
100     mbHasStringRefSyntax(false)
101 {
102     setOpenCLConfigToDefault();
103 
104     // SAL _DEBUG(__FILE__ ":" << __LINE__ << ": ScCalcConfig::ScCalcConfig(): " << *this);
105 }
106 
107 void ScCalcConfig::setOpenCLConfigToDefault()
108 {
109     // Keep in order of opcode value, is that clearest? (Random order,
110     // at least, would make no sense at all.)
111     static const OpCodeSet pDefaultOpenCLSubsetOpCodes(new o3tl::sorted_vector<OpCode>({
112         ocAdd,
113         ocSub,
114         ocNegSub,
115         ocMul,
116         ocDiv,
117         ocPow,
118         ocRandom,
119         ocSin,
120         ocCos,
121         ocTan,
122         ocArcTan,
123         ocExp,
124         ocLn,
125         ocSqrt,
126         ocStdNormDist,
127         ocSNormInv,
128         ocRound,
129         ocPower,
130         ocSumProduct,
131         ocMin,
132         ocMax,
133         ocSum,
134         ocProduct,
135         ocAverage,
136         ocCount,
137         ocVar,
138         ocNormDist,
139         ocVLookup,
140         ocCorrel,
141         ocCovar,
142         ocPearson,
143         ocSlope,
144         ocSumIfs}));
145 
146     // Note that these defaults better be kept in sync with those in
147     // officecfg/registry/schema/org/openoffice/Office/Calc.xcs.
148     // Crazy.
149     mbOpenCLSubsetOnly = true;
150     mbOpenCLAutoSelect = true;
151     mnOpenCLMinimumFormulaGroupSize = 100;
152     mpOpenCLSubsetOpCodes = pDefaultOpenCLSubsetOpCodes;
153 }
154 
155 void ScCalcConfig::reset()
156 {
157     *this = ScCalcConfig();
158 }
159 
160 void ScCalcConfig::MergeDocumentSpecific( const ScCalcConfig& r )
161 {
162     // String conversion options are per document.
163     meStringConversion       = r.meStringConversion;
164     mbEmptyStringAsZero      = r.mbEmptyStringAsZero;
165     // INDIRECT ref syntax is per document.
166     meStringRefAddressSyntax = r.meStringRefAddressSyntax;
167     mbHasStringRefSyntax      = r.mbHasStringRefSyntax;
168 }
169 
170 void ScCalcConfig::SetStringRefSyntax( formula::FormulaGrammar::AddressConvention eConv )
171 {
172     meStringRefAddressSyntax = eConv;
173     mbHasStringRefSyntax = true;
174 }
175 
176 bool ScCalcConfig::operator== (const ScCalcConfig& r) const
177 {
178     return meStringRefAddressSyntax == r.meStringRefAddressSyntax &&
179            meStringConversion == r.meStringConversion &&
180            mbEmptyStringAsZero == r.mbEmptyStringAsZero &&
181            mbHasStringRefSyntax == r.mbHasStringRefSyntax &&
182            mbOpenCLSubsetOnly == r.mbOpenCLSubsetOnly &&
183            mbOpenCLAutoSelect == r.mbOpenCLAutoSelect &&
184            maOpenCLDevice == r.maOpenCLDevice &&
185            mnOpenCLMinimumFormulaGroupSize == r.mnOpenCLMinimumFormulaGroupSize &&
186            *mpOpenCLSubsetOpCodes == *r.mpOpenCLSubsetOpCodes;
187 }
188 
189 bool ScCalcConfig::operator!= (const ScCalcConfig& r) const
190 {
191     return !operator==(r);
192 }
193 
194 OUString ScOpCodeSetToSymbolicString(const ScCalcConfig::OpCodeSet& rOpCodes)
195 {
196     OUStringBuffer result(256);
197     formula::FormulaCompiler aCompiler;
198     formula::FormulaCompiler::OpCodeMapPtr pOpCodeMap(aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH));
199 
200     for (auto i = rOpCodes->begin(); i != rOpCodes->end(); ++i)
201     {
202         if (i != rOpCodes->begin())
203             result.append(';');
204         result.append(pOpCodeMap->getSymbol(*i));
205     }
206 
207     return result.toString();
208 }
209 
210 ScCalcConfig::OpCodeSet ScStringToOpCodeSet(const OUString& rOpCodes)
211 {
212     ScCalcConfig::OpCodeSet result = std::make_shared<o3tl::sorted_vector< OpCode >>();
213     formula::FormulaCompiler aCompiler;
214     formula::FormulaCompiler::OpCodeMapPtr pOpCodeMap(aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH));
215 
216     const formula::OpCodeHashMap& rHashMap(pOpCodeMap->getHashMap());
217 
218     sal_Int32 fromIndex(0);
219     sal_Int32 semicolon;
220     OUString s(rOpCodes + ";");
221 
222     while ((semicolon = s.indexOf(';', fromIndex)) >= 0)
223     {
224         if (semicolon > fromIndex)
225         {
226             OUString element(s.copy(fromIndex, semicolon - fromIndex));
227             sal_Int32 n = element.toInt32();
228             if (n > 0 || (n == 0 && element == "0"))
229                 result->insert(static_cast<OpCode>(n));
230             else
231             {
232                 auto opcode(rHashMap.find(element));
233                 if (opcode != rHashMap.end())
234                     result->insert(opcode->second);
235                 else
236                     SAL_WARN("sc.opencl", "Unrecognized OpCode " << element << " in OpCode set string");
237             }
238         }
239         fromIndex = semicolon+1;
240     }
241     // HACK: Both unary and binary minus have the same string but different opcodes.
242     if( result->find( ocSub ) != result->end())
243         result->insert( ocNegSub );
244     return result;
245 }
246 
247 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
248