[Libreoffice-commits] core.git: Branch 'feature/unitver' - 1990 commits - accessibility/inc accessibility/source android/Bootstrap android/CustomTarget_android_desktop.mk android/CustomTarget_lo_android.mk android/experimental android/Makefile android/mobile-config.py avmedia/inc avmedia/source basctl/inc basctl/source basebmp/source basebmp/test basegfx/inc basegfx/source basic/inc basic/Library_sb.mk basic/qa basic/source basic/util bean/native binaryurp/source bin/extract-tooltip.py bin/get-bugzilla-attachments-by-mimetype bin/run bridges/inc bridges/Library_cpp_uno.mk bridges/source canvas/Library_cairocanvas.mk canvas/Library_oglcanvas.mk canvas/source canvas/workben chart2/CppunitTest_chart2_export.mk chart2/CppunitTest_chart2_import.mk chart2/CppunitTest_chart2_xshape.mk chart2/inc chart2/Library_chartcore.mk chart2/qa chart2/source cli_ure/readme.txt cli_ure/source codemaker/source comphelper/inc comphelper/source compilerplugins/clang config_host/config_global.h.in config_host.mk.in conf igmgr/inc configmgr/Module_configmgr.mk configmgr/qa configmgr/source configure.ac connectivity/CppunitTest_connectivity_ado.mk connectivity/CppunitTest_connectivity_commontools.mk connectivity/inc connectivity/Library_ado.mk connectivity/Module_connectivity.mk connectivity/qa connectivity/source cppcanvas/CppunitTest_cppcanvas_emfplus.mk cppcanvas/inc cppcanvas/qa cppcanvas/source cppuhelper/inc cppuhelper/source cppu/source cui/inc cui/Library_cui.mk cui/source cui/uiconfig cui/UIConfig_cui.mk dbaccess/CppunitTest_dbaccess_dialog_save.mk dbaccess/CppunitTest_dbaccess_embeddeddb_performancetest.mk dbaccess/CppunitTest_dbaccess_empty_stdlib_save.mk dbaccess/CppunitTest_dbaccess_firebird_test.mk dbaccess/CppunitTest_dbaccess_hsqldb_test.mk dbaccess/CppunitTest_dbaccess_macros_test.mk dbaccess/CppunitTest_dbaccess_nolib_save.mk dbaccess/CppunitTest_dbaccess_RowSetClones.mk dbaccess/inc dbaccess/Library_dbu.mk dbaccess/Module_dbaccess.mk dbaccess/PythonTest_dbaccess_python.mk dbaccess/ qa dbaccess/source dbaccess/util desktop/inc desktop/Library_deploymentgui.mk desktop/Library_sofficeapp.mk desktop/Package_scripts.mk desktop/source desktop/test desktop/uiconfig desktop/unx distro-configs/LibreOfficeCoverity.conf distro-configs/LibreOfficeWin32.conf distro-configs/LibreOfficeWin64.conf download.lst drawinglayer/inc drawinglayer/source dtrans/source editeng/inc editeng/source embeddedobj/Library_emboleobj.mk embeddedobj/source embeddedobj/test embedserv/Library_emser.mk embedserv/source extensions/inc extensions/Library_oleautobridge.mk extensions/source external/boost external/coinmp external/cppunit external/firebird external/graphite external/harfbuzz external/hunspell external/icu external/jpeg-turbo external/lcms2 external/libabw external/libatomic_ops external/libcdr external/libebook external/libgltf external/libmspub external/libmwaw external/libodfgen external/liborcus external/libpagemaker external/librevenge external/libvisio external/libwpd external/lps olve external/Module_external.mk external/poppler external/python3 external/redland external/udunits2 filter/Library_pdffilter.mk filter/Library_textfd.mk filter/source filter/uiconfig forms/Library_frm.mk forms/source forms/util formula/source fpicker/source fpicker/uiconfig framework/inc framework/qa framework/source g .git-hooks/commit-msg .git-hooks/post-merge helpcompiler/source helpcontent2 hwpfilter/inc hwpfilter/source i18npool/inc i18npool/source i18npool/util icon-themes/breeze icon-themes/crystal icon-themes/galaxy icon-themes/hicontrast icon-themes/human icon-themes/industrial icon-themes/oxygen icon-themes/sifr icon-themes/tango icon-themes/tango_testing idlc/inc idlc/source idl/inc idl/source include/avmedia include/basebmp include/basegfx include/basic include/canvas include/codemaker include/com include/comphelper include/connectivity include/cppcanvas include/cppu include/cppuhelper include/dbaccess include/drawinglayer include/editeng include/filter include/formula include/framework include/i18nlangtag include/i18nutil include/jvmaccess include/LibreOfficeKit include/linguistic include/o3tl include/oox include/opencl include/osl include/registry include/rtl include/sal include/salhelper include/sax include/sfx2 include/sot include/store include/svl include/svtools include/svx include/toolkit include/tools include/typelib include/ucbhelper include/uno include/unotest include/unotools include/vbahelper include/vcl include/xmloff include/xmlreader instsetoo_native/util ios/CustomTarget_TiledLibreOffice_app.mk ios/experimental ios/shared javaunohelper/com jurt/com jvmaccess/source jvmfwk/plugins jvmfwk/source l10ntools/inc l10ntools/source libreofficekit/qa libreofficekit/README libreofficekit/source lingucomponent/source linguistic/inc linguistic/source lotuswordpro/source Makefile.fetch Makefile.in mysqlc/source nlpsolver/src nlpsolver/ThirdParty odk/config odk/examples odk/Package_odk_headers.mk odk/source offapi/com offapi/UnoApi_offapi.mk of ficecfg/registry oox/inc oox/README oox/source opencl/inc opencl/source package/inc package/source postprocess/CppunitTest_services.mk postprocess/qa pyuno/inc pyuno/source qadevOOo/runner qadevOOo/tests readlicense_oo/license README.Android registry/source registry/tools registry/workben reportbuilder/java reportdesign/inc reportdesign/source RepositoryExternal.mk RepositoryFixes.mk Repository.mk RepositoryModule_host.mk rsc/inc rsc/source sal/cpprt sal/cppunittester sal/CppunitTest_sal_rtl_oustringbuffer.mk sal/inc sal/Library_sal.mk sal/osl sal/qa sal/rtl sal/textenc sal/workben sax/qa sax/source scaddins/source sc/AllLangResTarget_sc.mk sc/CppunitTest_sc_annotationobj.mk sc/CppunitTest_sc_annotationshapeobj.mk sc/CppunitTest_sc_annotationsobj.mk sc/CppunitTest_sc_bugfix_test.mk sc/CppunitTest_sc_cellrangeobj.mk sc/CppunitTest_sc_chart_regression_test.mk sc/CppunitTest_sc_condformats.mk sc/CppunitTest_sc_databaserangeobj.mk sc/CppunitTest_sc_datapilotfieldobj.mk sc/CppunitTest_sc _datapilottableobj.mk sc/CppunitTest_sc_editfieldobj_cell.mk sc/CppunitTest_sc_editfieldobj_header.mk sc/CppunitTest_sc_html_export_test.mk sc/CppunitTest_sc_macros_test.mk sc/CppunitTest_sc_modelobj.mk sc/CppunitTest_sc_namedrangeobj.mk sc/CppunitTest_sc_namedrangesobj.mk sc/CppunitTest_sc_opencl_test.mk sc/CppunitTest_sc_outlineobj.mk sc/CppunitTest_sc_perfobj.mk sc/CppunitTest_sc_rangelst_test.mk sc/CppunitTest_sc_recordchanges.mk sc/CppunitTest_sc_styleloaderobj.mk sc/CppunitTest_sc_tablesheetobj.mk sc/CppunitTest_sc_tablesheetsobj.mk sc/CppunitTest_sc_ucalc.mk sc/CppunitTest_sc_units.mk sc/inc sc/Library_sc.mk sc/Module_sc.mk scp2/AutoInstall.mk scp2/InstallModule_accessories.mk scp2/InstallModule_impress.mk scp2/InstallModule_ooo.mk scp2/InstallModule_python.mk scp2/InstallModule_xsltfilter.mk scp2/source sc/qa scripting/examples scripting/java scripting/source sc/source sc/uiconfig sc/workben sd/CppunitTest_sd_export_tests.mk sd/CppunitTest_sd_html_export_tests.mk sd/CppunitT est_sd_import_tests.mk sd/CppunitTest_sd_uimpress.mk sdext/inc sdext/source sd/inc sd/Module_sd.mk sd/qa sd/source sd/uiconfig setup_native/Library_sellangmsi.mk setup_native/Library_shlxtmsi.mk setup_native/Package_scripts.mk setup_native/source sfx2/inc sfx2/source sfx2/uiconfig shell/inc shell/Library_ooofilt.mk shell/Library_ooofilt_x64.mk shell/Library_propertyhdl.mk shell/Library_propertyhdl_x64.mk shell/Library_shlxthdl.mk shell/Library_shlxthdl_x64.mk shell/Package_scripts_kde.mk shell/source shell/StaticLibrary_shlxthandler_common.mk shell/StaticLibrary_shlxthandler_common_x64.mk slideshow/Library_OGLTrans.mk slideshow/source solenv/bin solenv/gbuild solenv/gdb solenv/inc soltools/mkdepend sot/inc sot/source starmath/inc starmath/Library_sm.mk starmath/qa starmath/source stoc/inc stoc/Library_bootstrap.mk stoc/Library_stocservices.mk stoc/source stoc/test stoc/util store/source svgio/inc svgio/source svl/inc svl/qa svl/source svl/unx svtools/inc svtools/qa svtools/source sv x/Executable_gengal.mk svx/inc svx/sdi svx/source svx/uiconfig sw/CppunitTest_sw_globalfilter.mk sw/CppunitTest_sw_htmlexport.mk sw/CppunitTest_sw_htmlimport.mk sw/CppunitTest_sw_layout_test.mk sw/CppunitTest_sw_macros_test.mk sw/CppunitTest_sw_mailmerge.mk sw/CppunitTest_sw_odfexport.mk sw/CppunitTest_sw_odfimport.mk sw/CppunitTest_sw_ooxmlfieldexport.mk sw/CppunitTest_sw_ooxmlimport.mk sw/CppunitTest_sw_ooxmlsdrexport.mk sw/CppunitTest_sw_ooxmlw14export.mk sw/CppunitTest_sw_rtfexport.mk sw/CppunitTest_sw_rtfimport.mk sw/CppunitTest_sw_uiwriter.mk sw/CppunitTest_sw_ww8export.mk sw/CppunitTest_sw_ww8import.mk sw/Executable_tiledrendering.mk sw/inc sw/Library_swd.mk sw/Library_sw.mk sw/Library_swui.mk sw/Module_sw.mk sw/ooxmlexport_setup.mk sw/qa sw/source sw/uiconfig sw/util test/source testtools/source toolkit/qa toolkit/source tools/inc tools/source translations ucb/source udkapi/com UnoControls/source unoidl/source unotest/Library_unobootstrapprotector.mk unotest/source unotools/ inc unotools/qa unotools/source unoxml/inc unoxml/source unusedcode.easy ure/source uui/inc uui/source vbahelper/inc vbahelper/source vcl/Executable_icontest.mk vcl/Executable_mtfdemo.mk vcl/Executable_ui-previewer.mk vcl/Executable_vcldemo.mk vcl/generic vcl/headless vcl/inc vcl/Library_vcl.mk vcl/Library_vclplug_gen.mk vcl/Library_vclplug_gtk3.mk vcl/Library_vclplug_svp.mk vcl/Module_vcl.mk vcl/null vcl/opengl vcl/osx vcl/qa vcl/quartz vcl/README vcl/source vcl/StaticLibrary_glxtest.mk vcl/StaticLibrary_headless.mk vcl/unx vcl/win vcl/WinResTarget_vcl.mk vcl/workben winaccessibility/source wizards/com wizards/source writerfilter/inc writerfilter/Library_writerfilter.mk writerfilter/source writerfilter/util writerperfect/Library_wpftimpress.mk writerperfect/source xmerge/source xmlhelp/source xmloff/inc xmloff/source xmlscript/dtd xmlscript/inc xmlscript/source xmlsecurity/inc xmlsecurity/source
Andrzej Hunt
andrzej at ahunt.org
Sun Mar 8 09:46:38 PDT 2015
Rebased ref, commits from common ancestor:
commit b8cf2f2195f1085bb22bed1ff7df0ec469abd53e
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Sun Mar 8 16:41:58 2015 +0000
Fix firebird linking.
Change-Id: Iab5d4e8d0c75310d9347ac910c45e6aaa8216ae1
diff --git a/external/udunits2/ExternalPackage_udunits2.mk b/external/udunits2/ExternalPackage_udunits2.mk
index a93da13..4073463 100644
--- a/external/udunits2/ExternalPackage_udunits2.mk
+++ b/external/udunits2/ExternalPackage_udunits2.mk
@@ -16,7 +16,9 @@ $(eval $(call gb_ExternalPackage_add_file,udunits2,$(LIBO_LIB_FOLDER)/iudunits2.
else ifeq ($(OS),MACOSX)
$(eval $(call gb_ExternalPackage_add_file,udunits2,$(LIBO_LIB_FOLDER)/libudunits2.dylib,lib/.libs/libudunits2.dylib))
else
-$(eval $(call gb_ExternalPackage_add_file,udunits2,$(LIBO_LIB_FOLDER)/libudunits2.so.0,lib/.libs/libudunits2.so.0.1.0))
+$(eval $(call gb_ExternalPackage_add_file,udunits2,$(LIBO_LIB_FOLDER)/libudunits2.so,lib/.libs/libudunits2.so))
+$(eval $(call gb_ExternalPackage_add_file,udunits2,$(LIBO_LIB_FOLDER)/libudunits2.so.0,lib/.libs/libudunits2.so.0))
+$(eval $(call gb_ExternalPackage_add_file,udunits2,$(LIBO_LIB_FOLDER)/libudunits2.so.0.1.0,lib/.libs/libudunits2.so.0.1.0))
endif
$(eval $(call gb_ExternalPackage_add_file,udunits2,$(LIBO_SHARE_FOLDER)/udunits2/udunits2.xml,lib/udunits2.xml))
commit f002de42cc9ee5c8e302a230562768b43ac28bc8
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Sun Mar 8 12:47:57 2015 +0000
Implement infobar warning for unit errors in a formula.
Change-Id: I2a909c9b71ff33754096da03d4cc7bbe390c2e1b
diff --git a/sc/AllLangResTarget_sc.mk b/sc/AllLangResTarget_sc.mk
index d41a13f..e6637ea 100644
--- a/sc/AllLangResTarget_sc.mk
+++ b/sc/AllLangResTarget_sc.mk
@@ -43,6 +43,7 @@ $(eval $(call gb_SrsTarget_add_files,sc/res,\
sc/source/ui/src/scstring.src \
sc/source/ui/src/filter.src \
sc/source/ui/src/condformatdlg.src \
+ sc/source/ui/src/units.src \
sc/source/ui/cctrl/checklistmenu.src \
sc/source/ui/navipi/navipi.src \
sc/source/ui/styleui/scstyles.src \
diff --git a/sc/inc/sc.hrc b/sc/inc/sc.hrc
index f76a45a..b484ae6 100644
--- a/sc/inc/sc.hrc
+++ b/sc/inc/sc.hrc
@@ -990,7 +990,14 @@
#define STR_TITLE_AUTHOR (STR_START + 442)
#define STR_TITLE_DATE (STR_START + 443)
#define STR_UNKNOWN_USER_CONFLICT (STR_START + 444)
-#define STR_END (STR_UNKNOWN_USER_CONFLICT)
+
+#define STR_UNITS_ERRORINCELL (STR_START + 450)
+#define BT_UNITS_EDIT_CELL (STR_START + 451)
+
+#define STR_UNITS_CONV_REQUIRED (STR_START + 455)
+#define BT_UNITS_CONV_THISCELL (STR_START + 456)
+#define BT_UNITS_CONV_ALL (STR_START + 457)
+#define STR_END (BT_UNITS_CONV_ALL)
#define BMP_START (STR_END)
diff --git a/sc/source/ui/inc/viewfunc.hxx b/sc/source/ui/inc/viewfunc.hxx
index 70c7e0f..ec7598a 100644
--- a/sc/source/ui/inc/viewfunc.hxx
+++ b/sc/source/ui/inc/viewfunc.hxx
@@ -368,6 +368,9 @@ private:
bool bAttrChanged, bool bAddUndo );
void MarkAndJumpToRanges(const ScRangeList& rRanges);
+
+ void NotifyUnitErrorInFormula( const ScAddress& rAddress, ScDocument* pDoc );
+ DECL_LINK( EditUnitErrorFormulaHandler, PushButton* );
};
#endif
diff --git a/sc/source/ui/src/units.src b/sc/source/ui/src/units.src
new file mode 100644
index 0000000..6165af8
--- /dev/null
+++ b/sc/source/ui/src/units.src
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "sc.hrc"
+
+String STR_UNITS_ERRORINCELL
+{
+ Text [ en-US ] = "Error in formula in Cell $1" ;
+};
+
+PushButton BT_UNITS_EDIT_CELL
+{
+ Pos = MAP_APPFONT( 0 , 0 );
+ Size = MAP_APPFONT( 70 , 0 );
+ Text[ en-US ] = "Edit Formula";
+};
+
+String STR_UNITS_CONV_REQUIRED
+{
+ Text [ en-US ] = "You entered data in $1, would you like to convert to $2?" ;
+};
+
+PushButton BT_UNITS_CONV_THISCELL
+{
+ Pos = MAP_APPFONT( 0 , 0 );
+ Size = MAP_APPFONT( 70 , 0 );
+ Text [ en-US ] = "Convert (only this cell)" ;
+};
+
+PushButton BT_UNITS_CONV_ALL
+{
+ Pos = MAP_APPFONT( 0 , 0 );
+ Size = MAP_APPFONT( 70 , 0 );
+ Text [ en-US ] = "Convert (automatically for all cells needing $2)" ;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx
index 1edd7a6..8d9dac7 100644
--- a/sc/source/ui/view/viewfunc.cxx
+++ b/sc/source/ui/view/viewfunc.cxx
@@ -31,6 +31,7 @@
#include <editeng/scripttypeitem.hxx>
#include <editeng/justifyitem.hxx>
#include <sfx2/bindings.hxx>
+#include <sfx2/infobar.hxx>
#include <svl/zforlist.hxx>
#include <svl/zformat.hxx>
#include <vcl/msgbox.hxx>
@@ -473,6 +474,7 @@ void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
else
{
SAL_INFO( "sc.units", "verification failed" );
+ NotifyUnitErrorInFormula( aPos, pDoc );
}
#endif
} while ( bAgain );
@@ -2810,4 +2812,30 @@ void ScViewFunc::UpdateSelectionArea( const ScMarkData& rSel, ScPatternAttr* pAt
pTabViewShell->AdjustBlockHeight(false, const_cast<ScMarkData*>(&rSel));
}
+void ScViewFunc::NotifyUnitErrorInFormula( const ScAddress& rAddress, ScDocument* pDoc )
+{
+ SfxViewFrame* pViewFrame = GetViewData().GetViewShell()->GetFrame();
+
+ OUString sTitle = SC_RESSTR( STR_UNITS_ERRORINCELL );
+ sTitle = sTitle.replaceAll( "$1", rAddress.GetColRowString() );
+ OUString sCellAddress = rAddress.Format( SCA_BITS, pDoc );
+ SfxInfoBarWindow* pInfoBar = pViewFrame->AppendInfoBar( sCellAddress, sTitle );
+
+ assert( pInfoBar );
+ PushButton* pButtonGotoCell = new PushButton( &pViewFrame->GetWindow(), ScResId(BT_UNITS_EDIT_CELL) );
+ pButtonGotoCell->SetClickHdl( LINK( this, ScViewFunc, EditUnitErrorFormulaHandler ) );
+ pInfoBar->addButton( pButtonGotoCell);
+}
+
+IMPL_LINK( ScViewFunc, EditUnitErrorFormulaHandler, PushButton*, pButton )
+{
+ SfxInfoBarWindow* pInfoBar = dynamic_cast< SfxInfoBarWindow* >( pButton->GetParent() );
+ const OUString sCell = pInfoBar->getId();
+ ScAddress aAddress;
+ aAddress.Parse( sCell );
+
+ (void) aAddress; // TODO: implement
+ return 0;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 229d884c20fb2b4e4e614ab29ed6771d7e23b9a6
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Sun Mar 8 12:42:47 2015 +0000
Make SfxInfoBarWindow SFX2_DLLPUBLIC.
We need this to be able to add buttons to the infobar from calc
(for use in the unit verification UI).
Change-Id: Iaffab714f926b8d70d047215e62fa7fd48eff832
diff --git a/include/sfx2/infobar.hxx b/include/sfx2/infobar.hxx
index eb0c4c6..0a15d33 100644
--- a/include/sfx2/infobar.hxx
+++ b/include/sfx2/infobar.hxx
@@ -40,7 +40,7 @@ class SFX2_DLLPUBLIC SfxInfoBarContainerChild : public SfxChildWindow
/** Class representing a single InfoBar to be added in a SfxInfoBarContainerWindow.
*/
-class SfxInfoBarWindow : public vcl::Window
+class SFX2_DLLPUBLIC SfxInfoBarWindow : public vcl::Window
{
private:
OUString m_sId;
commit 829baed96abe8b116ae5a8709c24e04b2d35c286
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Fri Mar 6 09:16:53 2015 +0000
Extract units from headers for unit verification too.
The default for unit annotations would be to have them in column
headers, the per-cell annotation is less useful in practice.
Change-Id: I593e8c5846018686ac5a3fa1cf865a3676cb3900
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 898582a..4a055d4 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -301,15 +301,44 @@ UtUnit UnitsImpl::getUnitForRef(FormulaToken* pToken, const ScAddress& rFormulaA
// Addresses can/will be relative to the formula, for extracting
// units however we will need to get the absolute address (i.e.
// by adding the current address to the relative formula address).
- ScAddress aInputAddress = pRef->toAbs( rFormulaAddress );
+ const ScAddress aInputAddress = pRef->toAbs( rFormulaAddress );
OUString sUnitString = extractUnitStringForCell(aInputAddress, pDoc);
UtUnit aUnit;
- if (!UtUnit::createUnit(sUnitString, aUnit, mpUnitSystem)) {
- SAL_INFO("sc.units", "no unit obtained for token at cell " << aInputAddress.GetColRowString());
+ if (sUnitString.getLength() > 0 &&
+ UtUnit::createUnit(sUnitString, aUnit, mpUnitSystem)) {
+ return aUnit;
}
+ // Scan UPwards from the current cell to find a header. This is since we could potentially
+ // have two different sets of data sharing a column, hence finding the closest header is necessary.
+ ScAddress aAddress = aInputAddress;
+ while (aAddress.Row() > 1) {
+ aAddress.IncRow(-1);
+
+ // We specifically test for string cells as intervening data cells could have
+ // differently defined units of their own. (However as these intervening cells
+ // will have the unit stored in the number format it would be ignored when
+ // checking the cell's string anyway.)
+ if (pDoc->GetCellType(aAddress) == CELLTYPE_STRING &&
+ extractUnitFromHeaderString(pDoc->GetString(aAddress), aUnit)) {
+ // TODO: one potential problem is that we could have a text only "united" data cell
+ // (where the unit wasn't automatically extracted due to being entered via
+ // a different spreadsheet program).
+ // We could solve that maybe by trying the unit extraction for such cells first?
+ // (I.e if(extractUnitStringForCell(...)) -> do the splitUnitsFrom... dance.
+ //
+ // TODO: and what if there are multiple units in the header (for whatever reason?)?
+ // We can probably just warn the user that we'll be giving them garbage in that case?
+ return aUnit;
+ }
+ }
+
+ SAL_INFO("sc.units", "no unit obtained for token at cell " << aInputAddress.GetColRowString());
+
+ // We return the dimensionless unit 1 if we don't find any other data suggesting a unit.
+ UtUnit::createUnit("", aUnit, mpUnitSystem);
return aUnit;
}
commit f51fe1d5b856d899b14a65fbcc1bc98b679fbcf4
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Fri Mar 6 09:09:27 2015 +0000
Implement header extraction for non-bracketed units.
Change-Id: I6f0ec0b2d4cb73531f5adcfa5fd6d0630820e32e
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 2d6dca9..97acef6 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -262,6 +262,7 @@ void UnitsTest::testUnitFromHeaderExtraction() {
OUString sEmpty = "";
CPPUNIT_ASSERT(!mpUnitsImpl->extractUnitFromHeaderString(sEmpty, aUnit));
+ CPPUNIT_ASSERT(aUnit == UtUnit());
OUString sSimple = "bla bla [cm/s]";
CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sSimple, aUnit));
@@ -271,6 +272,32 @@ void UnitsTest::testUnitFromHeaderExtraction() {
UtUnit aTestUnit;
CPPUNIT_ASSERT(UtUnit::createUnit("cm/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
CPPUNIT_ASSERT(aUnit == aTestUnit);
+
+ OUString sFreeStanding = "bla bla kg/h";
+ CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sFreeStanding, aUnit));
+ CPPUNIT_ASSERT(UtUnit::createUnit("kg/h", aTestUnit, mpUnitsImpl->mpUnitSystem));
+ CPPUNIT_ASSERT(aUnit == aTestUnit);
+
+ OUString sFreeStandingWithSpaces = "bla bla m / s";
+ CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sFreeStandingWithSpaces, aUnit));
+ CPPUNIT_ASSERT(UtUnit::createUnit("m/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
+ CPPUNIT_ASSERT(aUnit == aTestUnit);
+
+ OUString sOperatorSeparated = "bla bla / t/s";
+ CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sOperatorSeparated, aUnit));
+ CPPUNIT_ASSERT(UtUnit::createUnit("t/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
+ CPPUNIT_ASSERT(aUnit == aTestUnit);
+
+ OUString sRoundBrackets = "bla bla (t/h)";
+ CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sRoundBrackets, aUnit));
+ CPPUNIT_ASSERT(UtUnit::createUnit("t/h", aTestUnit, mpUnitsImpl->mpUnitSystem));
+ CPPUNIT_ASSERT(aUnit == aTestUnit);
+
+ // This becomes more of a nightmare to support, so let's not bother for now.
+ // OUString sFreeStandingMixedSpaces = "bla bla m /s* kg";
+ // CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sFreeStanding, aUnit));
+ // CPPUNIT_ASSERT(UtUnit::createUnit("m/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
+ // CPPUNIT_ASSERT(aUnit == aTestUnit);
}
CPPUNIT_TEST_SUITE_REGISTRATION(UnitsTest);
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 49fb48b..898582a 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -254,7 +254,37 @@ bool UnitsImpl::extractUnitFromHeaderString(const OUString& rString, UtUnit& aUn
}
// 2. We try to check for any free-standing units by splitting the string and testing each split
- // TODO: do this.
+ const sal_Int32 nTokenCount = comphelper::string::getTokenCount(rString, ' ');
+ // There's an inherent limit to how well we can cope with various spacing issues here without
+ // a ton of computational complexity.
+ // E.g. by parsing in this way we might end up with unmatched parentheses which udunits won't like.
+ // for now operators have to be completely freestanding i.e. "cm / kg" or completely within a valid unit string e.g. "cm/kg"
+ const OUString sOperators = "/*"; // valid
+ OUString sCurrent = "";
+ for (sal_Int32 nToken = 0; nToken < nTokenCount; nToken++) {
+ OUString sToken = rString.getToken(nToken, ' ');
+ UtUnit aTestUnit;
+ if (UtUnit::createUnit(sToken, aTestUnit, mpUnitSystem) ||
+ // Only test for a separator character if we have already got something in our string, as
+ // some of the operators could be used as separators from description to unit
+ // (e.g. "a description / kg").
+ ((sCurrent.getLength() > 0) && (sToken.getLength() == 1) && (sOperators.indexOf(sToken[0]) != -1))) {
+ // we're repeatedly testing the string hence using an OUStringBuffer isn't of much use since there's
+ // no simple/efficient way of repeatedly getting a testable OUString from the buffer.
+ sCurrent += sToken;
+ } else if (sCurrent.getLength() > 0) {
+ // If we have units, followed by text, followed by units, we should still flag an error since
+ // that's ambiguous (unless the desired units are enclose in [] in which case we've
+ // already extracted these desired units in step 1 above.
+ break;
+ }
+ }
+
+ // We test the length to make sure we don't return the dimensionless unit 1 if we haven't found any units
+ // in the header.
+ if (sCurrent.getLength() && UtUnit::createUnit(sCurrent, aUnit, mpUnitSystem)) {
+ return true;
+ }
// 3. Give up
aUnit = UtUnit(); // assign invalid
commit f277fe07baa8835ea082e6a62ec0d0c6214e9cb6
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Fri Mar 6 08:20:24 2015 +0000
Implement basic header string unit extraction.
Some more complex types of headers (e.g. units not within brackets)
aren't yet implemented.
Change-Id: I3e4d1ca5fd80ad1bbd84218ed38141fbcfca13b6
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index f6a0aa0..2d6dca9 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -45,6 +45,7 @@ public:
void testUnitFromFormatStringExtraction();
void testUnitValueStringSplitting();
+ void testUnitFromHeaderExtraction();
CPPUNIT_TEST_SUITE(UnitsTest);
@@ -53,6 +54,7 @@ public:
CPPUNIT_TEST(testUnitFromFormatStringExtraction);
CPPUNIT_TEST(testUnitValueStringSplitting);
+ CPPUNIT_TEST(testUnitFromHeaderExtraction);
CPPUNIT_TEST_SUITE_END();
@@ -255,6 +257,22 @@ void UnitsTest::testUnitValueStringSplitting() {
CPPUNIT_ASSERT(sUnit == "kg");
}
+void UnitsTest::testUnitFromHeaderExtraction() {
+ UtUnit aUnit;
+
+ OUString sEmpty = "";
+ CPPUNIT_ASSERT(!mpUnitsImpl->extractUnitFromHeaderString(sEmpty, aUnit));
+
+ OUString sSimple = "bla bla [cm/s]";
+ CPPUNIT_ASSERT(mpUnitsImpl->extractUnitFromHeaderString(sSimple, aUnit));
+ // We need to test in Units (rather than testing Unit::getString()) as
+ // any given unit can have multiple string representations (and utunits defaults to
+ // representing e.g. cm as (I think) "0.01m").
+ UtUnit aTestUnit;
+ CPPUNIT_ASSERT(UtUnit::createUnit("cm/s", aTestUnit, mpUnitsImpl->mpUnitSystem));
+ CPPUNIT_ASSERT(aUnit == aTestUnit);
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(UnitsTest);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 5b972d6..49fb48b 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -13,15 +13,23 @@
#include "document.hxx"
#include "refdata.hxx"
+#include "stringutil.hxx"
#include "tokenarray.hxx"
+#include <comphelper/string.hxx>
#include <osl/file.hxx>
#include <osl/mutex.hxx>
#include <rtl/bootstrap.hxx>
#include <svl/zformat.hxx>
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <com/sun/star/util/SearchAlgorithms.hpp>
+#include <com/sun/star/util/SearchOptions.hpp>
+#include <com/sun/star/util/XTextSearch.hpp>
+
#include <boost/scoped_array.hpp>
+using namespace com::sun::star;
using namespace formula;
using namespace sc;
using namespace sc::units;
@@ -200,6 +208,59 @@ OUString UnitsImpl::extractUnitStringForCell(const ScAddress& rAddress, ScDocume
return extractUnitStringFromFormat(rFormatString);
}
+bool UnitsImpl::extractUnitFromHeaderString(const OUString& rString, UtUnit& aUnit) {
+ com::sun::star::uno::Reference<com::sun::star::uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+
+ uno::Reference<lang::XMultiServiceFactory> xFactory(xContext->getServiceManager(), uno::UNO_QUERY_THROW);
+
+ uno::Reference<util::XTextSearch> xSearch =
+ uno::Reference< util::XTextSearch >(
+ xFactory->createInstance(
+ "com.sun.star.util.TextSearch"), uno::UNO_QUERY_THROW);
+
+ util::SearchOptions aOptions;
+ aOptions.algorithmType = util::SearchAlgorithms_REGEXP ;
+ aOptions.searchFlag = util::SearchFlags::ALL_IGNORE_CASE;
+
+ // 1. We try to check for units contained within square brackets
+ // TODO: we should do a sanity check that there's only one such unit though (and fail if there are multiple).
+ // Since otherwise there's no way for us to know which unit is the intended one, hence we need to get
+ // the user to deconfuse us by correcting their header to only contain the intended unit.
+ aOptions.searchString = "\\[([^\\]]+)\\]"; // Grab the contents between [ and ].
+ xSearch->setOptions( aOptions );
+
+ util::SearchResult aResult;
+ sal_Int32 nStartPosition = rString.getLength();
+ while (nStartPosition) {
+ // Search from the back since units are more likely to be at the end of the header.
+ aResult = xSearch->searchBackward(rString, nStartPosition, 0);
+
+ // We have either 0 items (no match), or 2 (matched string + the group within)
+ if (aResult.subRegExpressions != 2) {
+ break;
+ } else {
+ // Confusingly (to me) when doing a backwards search we end up with: endOffset < startOffset.
+ // i.e. startOffset is the last character of the intended substring, endOffset the first character.
+ // We specifically grab the offsets for the first actual regex group, which are stored in [1], the indexes
+ // at [0] represent the whole matched string (i.e. including square brackets).
+ OUString sUnitString = rString.copy(aResult.endOffset[1], aResult.startOffset[1] - aResult.endOffset[1]);
+
+ if (UtUnit::createUnit(sUnitString, aUnit, mpUnitSystem)) {
+ return true;
+ }
+
+ nStartPosition = aResult.endOffset[0];
+ }
+ }
+
+ // 2. We try to check for any free-standing units by splitting the string and testing each split
+ // TODO: do this.
+
+ // 3. Give up
+ aUnit = UtUnit(); // assign invalid
+ return false;
+}
+
UtUnit UnitsImpl::getUnitForRef(FormulaToken* pToken, const ScAddress& rFormulaAddress,
ScDocument* pDoc) {
assert(pToken->GetType() == formula::svSingleRef);
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index f37ad5c..909916a 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -67,6 +67,7 @@ private:
UtUnit getOutputUnitsForOpCode(std::stack< UtUnit >& rUnitStack, const OpCode& rOpCode);
OUString extractUnitStringFromFormat(const OUString& rFormatString);
OUString extractUnitStringForCell(const ScAddress& rAddress, ScDocument* pDoc);
+ bool extractUnitFromHeaderString(const OUString& rString, UtUnit& aUnit);
UtUnit getUnitForRef(formula::FormulaToken* pToken,
const ScAddress& rFormulaAddress,
ScDocument* pDoc);
commit 5d0b71f4324949db0dbac1de0b5615f1ada290c9
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Thu Mar 5 19:30:32 2015 +0000
Add some const.
Change-Id: I8cabaa6b5fd2df6bbb7db4e9c0a209b6ab305b38
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 0fc7f85..5b972d6 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -191,7 +191,7 @@ OUString UnitsImpl::extractUnitStringFromFormat(const OUString& rFormatString) {
}
-OUString UnitsImpl::extractUnitStringForCell(ScAddress& rAddress, ScDocument* pDoc) {
+OUString UnitsImpl::extractUnitStringForCell(const ScAddress& rAddress, ScDocument* pDoc) {
sal_uInt32 nFormat = pDoc->GetNumberFormat(rAddress);
const SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat );
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index 1009a95..f37ad5c 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -66,7 +66,7 @@ public:
private:
UtUnit getOutputUnitsForOpCode(std::stack< UtUnit >& rUnitStack, const OpCode& rOpCode);
OUString extractUnitStringFromFormat(const OUString& rFormatString);
- OUString extractUnitStringForCell(ScAddress& rAddress, ScDocument* pDoc);
+ OUString extractUnitStringForCell(const ScAddress& rAddress, ScDocument* pDoc);
UtUnit getUnitForRef(formula::FormulaToken* pToken,
const ScAddress& rFormulaAddress,
ScDocument* pDoc);
commit 3e8a1d6f99e99572010eaa51f4c8b3fd0f191eec
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Sat Feb 7 11:33:39 2015 +0000
Implement actual unit extraction / format setting.
This makes the unit verification automatic (modulo no UI),
but is still very hacky.
Change-Id: Iff6f97b2c070e1caf2911533339cb3f07b259ed4
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 4c0bf2a..42e9db8 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1672,6 +1672,33 @@ bool ScColumn::ParseString(
{
if (aParam.mbDetectNumberFormat)
{
+#ifdef ENABLE_CALC_UNITVERIFICATION
+ OUString sValue, sUnit;
+ boost::shared_ptr< sc::units::Units > pUnits = sc::units::Units::GetUnits();
+ if (pUnits->splitUnitsFromInputString(rString, sValue, sUnit)) {
+ // TODO we should check whether a suitable format already exists.
+ // We also want to ideally preserve whatever format would be used
+ // for the actual numbers (e.g. 1E4 is preserved as 1E4 unless we
+ // now set the number format as #"foo" in which case the raw number is
+ // displayed i.e. 1000foo)
+
+ // I.e. it may be more sensible to extract the unit here, continue processing as normal
+ // and then at the end add the unit to the format. In fact we should probably ALWAYS
+ // remove the unit whenever doing any format processing (i.e. everywhere), and reappend it after?
+ // But in that case it would make sense to actually store units independently of number format.
+
+ // (This is all just a dirty hack for now...)
+ OUString sNewFormat = "#\"" + sUnit + "\"";
+ sal_uInt32 nFormatKey;
+ short nType = css::util::NumberFormat::DEFINED;
+ sal_Int32 nErrorPosition; // Unused, because we should be creating working number formats.
+
+ aParam.mpNumFormatter->PutEntry(sNewFormat, nErrorPosition, nType, nFormatKey);
+ SetNumberFormat(nRow, nFormatKey);
+
+ nIndex = nFormatKey;
+ }
+#endif
if (!aParam.mpNumFormatter->IsNumberFormat(rString, nIndex, nVal))
break;
commit 804a553367e565c3dc4a6c613e5ec813098fdfc9
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Fri Feb 6 18:46:30 2015 +0000
Implement splitUnitsFromInputString.
Change-Id: I1f781948e57c37dc3adbfcd14cb3d6ba488c10a0
diff --git a/sc/inc/units.hxx b/sc/inc/units.hxx
index cfeae9e..9561b40 100644
--- a/sc/inc/units.hxx
+++ b/sc/inc/units.hxx
@@ -10,6 +10,8 @@
#ifndef INCLUDED_SC_INC_UNITS_HXX
#define INCLUDED_SC_INC_UNITS_HXX
+#include <rtl/ustring.hxx>
+
#include <boost/shared_ptr.hpp>
class ScAddress;
@@ -27,6 +29,16 @@ public:
virtual bool verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAddress, ScDocument* pDoc) = 0;
+ /*
+ * Split the input into value and unit, where rInput == rValue + rUnit.
+ * (We assume that the unit is always the last part of the input string.)
+ *
+ * Returns whether or not the string has been split.
+ * rValue and rUnit are always set to valid values, irrespective of string
+ * splitting having actually taken place.
+ */
+ virtual bool splitUnitsFromInputString(const OUString& rInput, OUString& rValue, OUString& rUnit) = 0;
+
virtual ~Units() {}
};
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 3515254..f6a0aa0 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -38,15 +38,22 @@ public:
::boost::shared_ptr< UnitsImpl > mpUnitsImpl;
+
void testUTUnit();
void testUnitVerification();
void testUnitFromFormatStringExtraction();
+ void testUnitValueStringSplitting();
+
CPPUNIT_TEST_SUITE(UnitsTest);
+
CPPUNIT_TEST(testUTUnit);
CPPUNIT_TEST(testUnitVerification);
+
CPPUNIT_TEST(testUnitFromFormatStringExtraction);
+ CPPUNIT_TEST(testUnitValueStringSplitting);
+
CPPUNIT_TEST_SUITE_END();
private:
@@ -209,6 +216,45 @@ void UnitsTest::testUnitFromFormatStringExtraction() {
CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("#\"cm\"") == "cm");
}
+void UnitsTest::testUnitValueStringSplitting() {
+ OUString sValue, sUnit;
+
+ OUString sEmptyString = "";
+ CPPUNIT_ASSERT(!mpUnitsImpl->splitUnitsFromInputString(sEmptyString, sValue, sUnit));
+ CPPUNIT_ASSERT(sValue.isEmpty());
+ CPPUNIT_ASSERT(sUnit.isEmpty());
+
+ OUString sNumberOnlyString = "10";
+ CPPUNIT_ASSERT(!mpUnitsImpl->splitUnitsFromInputString(sNumberOnlyString, sValue, sUnit));
+ CPPUNIT_ASSERT(sValue == "10");
+ CPPUNIT_ASSERT(sUnit.isEmpty());
+
+ OUString sTextOnlyString = "hello world";
+ CPPUNIT_ASSERT(!mpUnitsImpl->splitUnitsFromInputString(sTextOnlyString, sValue, sUnit));
+ CPPUNIT_ASSERT(sValue == "hello world");
+ CPPUNIT_ASSERT(sUnit.isEmpty());
+
+ OUString sDeceptiveInput = "30garbage";
+ CPPUNIT_ASSERT(!mpUnitsImpl->splitUnitsFromInputString(sDeceptiveInput, sValue, sUnit));
+ CPPUNIT_ASSERT(sValue == "30garbage");
+ CPPUNIT_ASSERT(sUnit.isEmpty());
+
+ OUString sUnitOnly = "cm";
+ CPPUNIT_ASSERT(!mpUnitsImpl->splitUnitsFromInputString(sUnitOnly, sValue, sUnit));
+ CPPUNIT_ASSERT(sValue == "cm");
+ CPPUNIT_ASSERT(sUnit.isEmpty());
+
+ OUString sSimpleUnitedValue = "20m/s";
+ CPPUNIT_ASSERT(mpUnitsImpl->splitUnitsFromInputString(sSimpleUnitedValue, sValue, sUnit));
+ CPPUNIT_ASSERT(sValue == "20");
+ CPPUNIT_ASSERT(sUnit == "m/s");
+
+ OUString sMultipleTokens = "40E-4kg";
+ CPPUNIT_ASSERT(mpUnitsImpl->splitUnitsFromInputString(sMultipleTokens, sValue, sUnit));
+ CPPUNIT_ASSERT(sValue == "40E-4");
+ CPPUNIT_ASSERT(sUnit == "kg");
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(UnitsTest);
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index d51350e5..0fc7f85 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -282,5 +282,40 @@ bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAdd
return true;
}
+bool IsDigit(sal_Unicode c) {
+ return (c>= '0' && c <= '9');
+}
+
+bool UnitsImpl::splitUnitsFromInputString(const OUString& rInput, OUString& rValueOut, OUString& rUnitOut) {
+ int nPos = rInput.getLength();
+
+ while (nPos) {
+ if (IsDigit(rInput[nPos-1])) {
+ break;
+ }
+ nPos--;
+ }
+
+ rUnitOut = rInput.copy(nPos);
+
+ UtUnit aUnit;
+ // If the entire input is a string (nPos == 0) then treating it as a unit
+ // makes little sense as there is no numerical value associated with it.
+ // Hence it makes sense to skip testing in this case.
+ // We also need to specifically ignore the no unit case (nPos == rInput.getLength())
+ // as otherwise we are obtaining the unit for "" which is a valid unit
+ // (the dimensionless) unit, even though in reality we should obtain no unit
+ // and return false.
+ if ((nPos < rInput.getLength())
+ && (nPos > 0)
+ && UtUnit::createUnit(rUnitOut, aUnit, mpUnitSystem)) {
+ rValueOut = rInput.copy(0, nPos);
+ return true;
+ } else {
+ rValueOut = rInput;
+ rUnitOut.clear();
+ return false;
+ }
+}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index 2561e1e..1009a95 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -61,6 +61,7 @@ public:
virtual ~UnitsImpl();
virtual bool verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAddress, ScDocument* pDoc) SAL_OVERRIDE;
+ virtual bool splitUnitsFromInputString(const OUString& rInput, OUString& rValue, OUString& rUnit) SAL_OVERRIDE;
private:
UtUnit getOutputUnitsForOpCode(std::stack< UtUnit >& rUnitStack, const OpCode& rOpCode);
commit 02cc21e8ebccd5c31e79b8476b114c096b90b640
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Thu Feb 5 20:21:37 2015 +0000
Move and rename string extraction test.
Change-Id: I39574e227b4d2f3ff0df8c36b1c96337d5fcbfb7
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 062262e..3515254 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -39,13 +39,14 @@ public:
::boost::shared_ptr< UnitsImpl > mpUnitsImpl;
void testUTUnit();
- void testStringExtraction();
void testUnitVerification();
+ void testUnitFromFormatStringExtraction();
+
CPPUNIT_TEST_SUITE(UnitsTest);
CPPUNIT_TEST(testUTUnit);
- CPPUNIT_TEST(testStringExtraction);
CPPUNIT_TEST(testUnitVerification);
+ CPPUNIT_TEST(testUnitFromFormatStringExtraction);
CPPUNIT_TEST_SUITE_END();
private:
@@ -98,11 +99,6 @@ void UnitsTest::testUTUnit() {
CPPUNIT_ASSERT(aM/aS == aM_S);
}
-void UnitsTest::testStringExtraction() {
- CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("\"weight: \"0.0\"kg\"") == "kg");
- CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("#\"cm\"") == "cm");
-}
-
void UnitsTest::testUnitVerification() {
// Make sure we have at least one tab to work with
mpDoc->EnsureTable(0);
@@ -208,6 +204,11 @@ void UnitsTest::testUnitVerification() {
CPPUNIT_ASSERT(!mpUnitsImpl->verifyFormula(pTokens, address, mpDoc));
}
+void UnitsTest::testUnitFromFormatStringExtraction() {
+ CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("\"weight: \"0.0\"kg\"") == "kg");
+ CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("#\"cm\"") == "cm");
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(UnitsTest);
CPPUNIT_PLUGIN_IMPLEMENT();
commit 92ee1b3f931d420594aa270941f387df3513ec22
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Thu Feb 5 18:17:09 2015 +0000
Add stream-printing operator<< for UtUnit.
Change-Id: I1c36415d673841683fe9007f17bee110a494baa7
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 344c0d1..d51350e5 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -139,7 +139,7 @@ UtUnit UnitsImpl::getOutputUnitsForOpCode(stack< UtUnit >& rUnitStack, const OpC
if (pFirstUnit == pSecondUnit) {
// The two units are identical, hence we can return either.
pOut = pFirstUnit;
- SAL_INFO("sc.units", "verified equality for unit " << pFirstUnit.getString());
+ SAL_INFO("sc.units", "verified equality for unit " << pFirstUnit);
} else {
// TODO: notify/link UI.
}
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index b79c057..97b6305 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -93,6 +93,13 @@ public:
}
};
+template< typename charT, typename traits >
+inline std::basic_ostream<charT, traits> & operator <<(
+ std::basic_ostream<charT, traits> & stream, const UtUnit& rUnit )
+{
+ return stream << "[" << rUnit.getString() << "]";
+}
+
}} // namespace sc::units
#endif // INCLUDED_SC_SOURCE_CORE_UNITS_UTUNIT_HXX
commit dddf872997e910893c5e3338264cd10cc3fc02ec
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Thu Feb 5 17:56:13 2015 +0000
Add some tests for our UtUnit arithmetic operators.
Change-Id: I5a29bdf306ec1980b44f54b161d0ba7df0f1a83e
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index c5013c1..062262e 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -85,6 +85,17 @@ void UnitsTest::testUTUnit() {
// Test that we can't create garbage units
UtUnit aGarbage;
CPPUNIT_ASSERT(!UtUnit::createUnit("garbage", aGarbage, mpUnitsImpl->mpUnitSystem));
+
+ // Do some addition, subtraction, comparison tests.
+ UtUnit aM;
+ UtUnit::createUnit("m", aM, mpUnitsImpl->mpUnitSystem);
+ UtUnit aS;
+ UtUnit::createUnit("s", aS, mpUnitsImpl->mpUnitSystem);
+ UtUnit aM_S;
+ UtUnit::createUnit("m/s", aM_S, mpUnitsImpl->mpUnitSystem);
+
+ CPPUNIT_ASSERT(aM_S*aS == aM);
+ CPPUNIT_ASSERT(aM/aS == aM_S);
}
void UnitsTest::testStringExtraction() {
commit b4b4f1767226e5a79254d75d4c94e440993f7734
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Thu Feb 5 17:51:07 2015 +0000
Hide raw ut_unit access in UTUnit.
UtUnit exists to hide all raw fiddling with ut_unit pointers
and the various ut_* methods.
We also get rid of the implicit bool conversion and instead have
and explicit method to check whether or not we hold a valid unit
to make things more obvious.
Change-Id: I654c47ba6daf8dfc4c2cc648150b6a86b90195bc
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index adb5d65..344c0d1 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -89,7 +89,7 @@ UtUnit UnitsImpl::getOutputUnitsForOpCode(stack< UtUnit >& rUnitStack, const OpC
if (!(rUnitStack.size() >= 1)) {
SAL_WARN("sc.units", "no units on stack for unary operation");
- return 0;
+ return UtUnit();
}
UtUnit pUnit = rUnitStack.top();
@@ -97,8 +97,8 @@ UtUnit UnitsImpl::getOutputUnitsForOpCode(stack< UtUnit >& rUnitStack, const OpC
switch (rOpCode) {
case ocNot:
- if (!ut_is_dimensionless(pUnit.get())) {
- return 0;
+ if (!pUnit.isDimensionless()) {
+ return UtUnit();
}
// We just keep the same unit (in this case no unit) so can
// fall through.
@@ -123,7 +123,7 @@ UtUnit UnitsImpl::getOutputUnitsForOpCode(stack< UtUnit >& rUnitStack, const OpC
SAL_WARN("sc.units", "less than two units on stack when attempting binary operation");
// TODO: what should we be telling the user in this case? Can this even happen (i.e.
// should we just be asserting here?)
- return 0;
+ return UtUnit();
}
UtUnit pSecondUnit = rUnitStack.top();
@@ -212,31 +212,14 @@ UtUnit UnitsImpl::getUnitForRef(FormulaToken* pToken, const ScAddress& rFormulaA
// by adding the current address to the relative formula address).
ScAddress aInputAddress = pRef->toAbs( rFormulaAddress );
- // udunits requires strings to be trimmed before parsing -- it's easiest to do this
- // using the OUString utils (as opposed to using ut_trim once we have a c string.
- OUString sUnitString = extractUnitStringForCell(aInputAddress, pDoc).trim();
+ OUString sUnitString = extractUnitStringForCell(aInputAddress, pDoc);
- // empty string == dimensionless unit. ut_parse returns an error for an empty string
- // hence we need to manually detect that case and return the dimensionless unit.
- if (sUnitString.getLength() == 0) {
- SAL_INFO("sc.units", "empty unit string: returning dimensionless unit");
- return UtUnit(ut_get_dimensionless_unit_one(mpUnitSystem.get()));
- }
-
- SAL_INFO("sc.units", "got unit string [" << sUnitString << "]");
- OString sUnitStringUTF8 = OUStringToOString(sUnitString, RTL_TEXTENCODING_UTF8);
-
- // TODO: we should probably have a cache of unit strings here to save reparsing
- // on every run?
-
- UtUnit pUnit(ut_parse(mpUnitSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8));
-
- if (!pUnit) {
+ UtUnit aUnit;
+ if (!UtUnit::createUnit(sUnitString, aUnit, mpUnitSystem)) {
SAL_INFO("sc.units", "no unit obtained for token at cell " << aInputAddress.GetColRowString());
- SAL_INFO("sc.units", "error encountered: " << getUTStatus());
}
- return pUnit;
+ return aUnit;
}
// getUnitForRef: check format -> if not in format, use more complicated method? (Format overrides header definition)
@@ -255,7 +238,7 @@ bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAdd
{
UtUnit pUnit(getUnitForRef(pToken, rFormulaAddress, pDoc));
- if (!pUnit) {
+ if (!pUnit.isValid()) {
SAL_INFO("sc.units", "no unit returned for scSingleRef, ut_status: " << getUTStatus());
// This only happens in case of parsing (or system) errors.
@@ -275,7 +258,7 @@ bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAdd
// A null unit indicates either invalid units and/or other erronous input
// i.e. is an indication that getOutputUnitsForOpCode failed.
- if (pOut) {
+ if (pOut.isValid()) {
aUnitStack.push(pOut);
} else {
return false;
diff --git a/sc/source/core/units/utunit.cxx b/sc/source/core/units/utunit.cxx
index 1cd64b7..f539502 100644
--- a/sc/source/core/units/utunit.cxx
+++ b/sc/source/core/units/utunit.cxx
@@ -20,7 +20,7 @@ bool UtUnit::createUnit(const OUString& rUnitString, UtUnit& rUnitOut, const boo
UtUnit pParsedUnit(ut_parse(pUTSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8));
- if (pParsedUnit) {
+ if (pParsedUnit.isValid()) {
rUnitOut = pParsedUnit;
return true;
} else {
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index c1df459..b79c057 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -42,29 +42,40 @@ private:
ut_free(pUnit);
}
+ UtUnit(ut_unit* pUnit):
+ mpUnit(pUnit, &freeUt)
+ {}
+
+ void reset(ut_unit* pUnit) {
+ mpUnit.reset(pUnit, &freeUt);
+ }
+
+ ut_unit* get() const {
+ return mpUnit.get();
+ }
+
public:
static bool createUnit(const OUString& rUnitString, UtUnit& rUnitOut, const boost::shared_ptr< ut_system >& pUTSystem);
- UtUnit(ut_unit* pUnit = 0):
- mpUnit(pUnit, &freeUt)
- {}
+ /*
+ * Default constructor returns an empty/invalid unit.
+ * (Note: this is different from the dimensionless unit which is valid.)
+ */
+ UtUnit() {};
UtUnit(const UtUnit& rUnit):
mpUnit(rUnit.mpUnit)
{}
- void reset(ut_unit* pUnit) {
- mpUnit.reset(pUnit, &freeUt);
- }
-
OUString getString() const;
- ut_unit* get() const {
- return mpUnit.get();
+ bool isValid() {
+ // We use a null pointer/empty unit to indicate an invalid unit.
+ return mpUnit.get() != 0;
}
- explicit operator bool() const {
- return mpUnit.operator bool();
+ bool isDimensionless() const {
+ return ut_is_dimensionless(this->get());
}
bool operator==(const UtUnit& rUnit) {
commit 2d90a72090e8d08c9a6eecb86a05fc069e62f105
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Thu Feb 5 17:45:01 2015 +0000
Trim unit string for ut_parse.
Change-Id: I83c32e5336b589174db92123c4959db4ac8624c8
diff --git a/sc/source/core/units/utunit.cxx b/sc/source/core/units/utunit.cxx
index 7061658..1cd64b7 100644
--- a/sc/source/core/units/utunit.cxx
+++ b/sc/source/core/units/utunit.cxx
@@ -14,7 +14,9 @@
using namespace sc::units;
bool UtUnit::createUnit(const OUString& rUnitString, UtUnit& rUnitOut, const boost::shared_ptr< ut_system >& pUTSystem) {
- OString sUnitStringUTF8 = OUStringToOString(rUnitString, RTL_TEXTENCODING_UTF8);
+ // ut_parse requires the string to be trimmed of whitespace, it's
+ // simplest just to do this during conversion:
+ OString sUnitStringUTF8 = OUStringToOString(rUnitString.trim(), RTL_TEXTENCODING_UTF8);
UtUnit pParsedUnit(ut_parse(pUTSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8));
commit 9a0b066620dc11c52406ad6e3530e303a58e7bd3
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Thu Feb 5 17:24:27 2015 +0000
Add some basic tests for UtUnit creation.
Change-Id: Ic4d2e26b383fa07b53757dd755508d42dcf88593
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index e8ec0ab..c5013c1 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -8,6 +8,7 @@
*/
#include "unitsimpl.hxx"
+#include "utunit.hxx"
#include "formulacell.hxx"
@@ -37,10 +38,12 @@ public:
::boost::shared_ptr< UnitsImpl > mpUnitsImpl;
+ void testUTUnit();
void testStringExtraction();
void testUnitVerification();
CPPUNIT_TEST_SUITE(UnitsTest);
+ CPPUNIT_TEST(testUTUnit);
CPPUNIT_TEST(testStringExtraction);
CPPUNIT_TEST(testUnitVerification);
CPPUNIT_TEST_SUITE_END();
@@ -69,6 +72,21 @@ void UnitsTest::tearDown() {
BootstrapFixture::tearDown();
}
+void UnitsTest::testUTUnit() {
+ // Test that we can create units.
+ UtUnit aDimensionless;
+ CPPUNIT_ASSERT(UtUnit::createUnit("", aDimensionless, mpUnitsImpl->mpUnitSystem));
+ // And test that an empty string does in fact map to the dimensionless unit one.
+ // The documentation states that ut_is_dimensionless returns zero for dimensionless
+ // units, however the sources (and udunits2's unit tests) suggest that zero is returned
+ // for a unit WITH dimensions (as the method name would suggest).
+ CPPUNIT_ASSERT(ut_is_dimensionless(aDimensionless.mpUnit.get()) != 0);
+
+ // Test that we can't create garbage units
+ UtUnit aGarbage;
+ CPPUNIT_ASSERT(!UtUnit::createUnit("garbage", aGarbage, mpUnitsImpl->mpUnitSystem));
+}
+
void UnitsTest::testStringExtraction() {
CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("\"weight: \"0.0\"kg\"") == "kg");
CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("#\"cm\"") == "cm");
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index c075d00..c1df459 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -19,6 +19,10 @@
namespace sc {
namespace units {
+namespace test {
+ class UnitsTest;
+}
+
/*
* Convenience shared_ptr wrapper for ut_unit, which takes
* care of dealing with the necessary custom deleter.
@@ -29,6 +33,8 @@ namespace units {
* wrapper.
*/
class UtUnit {
+ friend class test::UnitsTest;
+
private:
::boost::shared_ptr< ut_unit > mpUnit;
commit 467efa0a76870a87b2e607bcc5e74b2c29e58817
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Thu Feb 5 17:23:50 2015 +0000
Add factory method for UtUnit creation when parsing.
Change-Id: I1e812fc9f2dfaeccb7a6c65f3739f3be6e206760
diff --git a/sc/source/core/units/utunit.cxx b/sc/source/core/units/utunit.cxx
index 74e28b6..7061658 100644
--- a/sc/source/core/units/utunit.cxx
+++ b/sc/source/core/units/utunit.cxx
@@ -13,6 +13,21 @@
using namespace sc::units;
+bool UtUnit::createUnit(const OUString& rUnitString, UtUnit& rUnitOut, const boost::shared_ptr< ut_system >& pUTSystem) {
+ OString sUnitStringUTF8 = OUStringToOString(rUnitString, RTL_TEXTENCODING_UTF8);
+
+ UtUnit pParsedUnit(ut_parse(pUTSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8));
+
+ if (pParsedUnit) {
+ rUnitOut = pParsedUnit;
+ return true;
+ } else {
+ SAL_INFO("sc.units", "error encountered parsing unit \"" << rUnitString << "\": " << getUTStatus());
+ return false;
+ }
+}
+
+
OUString UtUnit::getString() const {
char aBuf[200];
int nChars = ut_format(mpUnit.get(), aBuf, 200, UT_UTF8);
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index a209cf9..c075d00 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -37,6 +37,8 @@ private:
}
public:
+ static bool createUnit(const OUString& rUnitString, UtUnit& rUnitOut, const boost::shared_ptr< ut_system >& pUTSystem);
+
UtUnit(ut_unit* pUnit = 0):
mpUnit(pUnit, &freeUt)
{}
commit d4390310dc0b50c7c839ba761a03b2517685c65d
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Wed Jan 28 18:18:27 2015 +0000
Implement operators * and / for UtUnit.
This will save more convoluted calls to ut_multiply/divide
when implementing further opcodes.
Change-Id: I022bd8aad4a8165a68534730447a0c8b9c8f4aba
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 19feff0..adb5d65 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -145,10 +145,10 @@ UtUnit UnitsImpl::getOutputUnitsForOpCode(stack< UtUnit >& rUnitStack, const OpC
}
break;
case ocMul:
- pOut.reset(ut_multiply(pFirstUnit.get(), pSecondUnit.get()));
+ pOut = pFirstUnit * pSecondUnit;
break;
case ocDiv:
- pOut.reset(ut_divide(pFirstUnit.get(), pSecondUnit.get()));
+ pOut = pFirstUnit / pSecondUnit;
break;
default:
SAL_INFO("sc.units", "unit verification not supported for opcode: " << nOpCode);
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index 5f77e84..a209cf9 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -62,6 +62,16 @@ public:
bool operator==(const UtUnit& rUnit) {
return ut_compare(this->get(), rUnit.get()) == 0;
}
+
+ UtUnit operator*(const UtUnit& rUnit) {
+ return UtUnit(ut_multiply(this->get(), rUnit.get()));
+ }
+
+ UtUnit operator/(const UtUnit& rUnit) {
+ // the parameter is the right hand side value in the operation,
+ // i.e. we are working with this / rUnit.
+ return UtUnit(ut_divide(this->get(), rUnit.get()));
+ }
};
}} // namespace sc::units
commit 7953e75717122d23cf26090d00285b3e21a8231c
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Wed Jan 28 17:48:47 2015 +0000
Implement operator== for UtUnit
(Although this only replaces one call to ut_compare, implementation
of further opcodes/further functionality will likely require much
more comparison. We should probably also have an operator* and operator/
in future.)
Change-Id: Ib31d4dec753823260e9905446ac3d5cd7eb720c8
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index a2c5581..19feff0 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -136,7 +136,7 @@ UtUnit UnitsImpl::getOutputUnitsForOpCode(stack< UtUnit >& rUnitStack, const OpC
// Adding and subtracting both require the same units on both sides
// hence we can just fall through / use the same logic.
case ocSub:
- if (ut_compare(pFirstUnit.get(), pSecondUnit.get()) == 0) {
+ if (pFirstUnit == pSecondUnit) {
// The two units are identical, hence we can return either.
pOut = pFirstUnit;
SAL_INFO("sc.units", "verified equality for unit " << pFirstUnit.getString());
diff --git a/sc/source/core/units/utunit.hxx b/sc/source/core/units/utunit.hxx
index 6ad2719..5f77e84 100644
--- a/sc/source/core/units/utunit.hxx
+++ b/sc/source/core/units/utunit.hxx
@@ -58,6 +58,10 @@ public:
explicit operator bool() const {
return mpUnit.operator bool();
}
+
+ bool operator==(const UtUnit& rUnit) {
+ return ut_compare(this->get(), rUnit.get()) == 0;
+ }
};
}} // namespace sc::units
commit 62db857d4d8e54291ec3919f43730a8ca98561ee
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Tue Jan 27 13:29:21 2015 +0000
Implement tests for unit verification.
Change-Id: I1fc97b2eeed404e897f4816fe65f05e14150de28
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 290e74a..e8ec0ab 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -9,8 +9,12 @@
#include "unitsimpl.hxx"
+#include "formulacell.hxx"
+
#include "helper/qahelper.hxx"
+#include <com/sun/star/util/NumberFormat.hpp>
+
using namespace sc::units;
// In order to be able to access the private members of UnitsImpl for
@@ -34,19 +38,34 @@ public:
::boost::shared_ptr< UnitsImpl > mpUnitsImpl;
void testStringExtraction();
+ void testUnitVerification();
CPPUNIT_TEST_SUITE(UnitsTest);
CPPUNIT_TEST(testStringExtraction);
+ CPPUNIT_TEST(testUnitVerification);
CPPUNIT_TEST_SUITE_END();
+
+private:
+ ScDocument *mpDoc;
+ ScDocShellRef m_xDocShRef;
};
void UnitsTest::setUp() {
BootstrapFixture::setUp();
+ ScDLL::Init();
+ m_xDocShRef = new ScDocShell(
+ SFXMODEL_STANDARD |
+ SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
+ SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
+
+ mpDoc = &m_xDocShRef->GetDocument();
+
mpUnitsImpl = UnitsImpl::GetUnits();
}
void UnitsTest::tearDown() {
+ m_xDocShRef.Clear();
BootstrapFixture::tearDown();
}
@@ -55,6 +74,111 @@ void UnitsTest::testStringExtraction() {
CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("#\"cm\"") == "cm");
}
+void UnitsTest::testUnitVerification() {
+ // Make sure we have at least one tab to work with
+ mpDoc->EnsureTable(0);
+
+ SvNumberFormatter* pFormatter = mpDoc->GetFormatTable();
+ sal_uInt32 nKeyCM, nKeyKG, nKeyS, nKeyCM_S;
+
+ // Used to return position of error in input string for PutEntry
+ // -- not needed here.
+ sal_Int32 nCheckPos;
+
+ short nType = css::util::NumberFormat::DEFINED;
+
+ OUString sCM = "#\"cm\"";
+ pFormatter->PutEntry(sCM, nCheckPos, nType, nKeyCM);
+ OUString sKG = "#\"kg\"";
+ pFormatter->PutEntry(sKG, nCheckPos, nType, nKeyKG);
+ OUString sS = "#\"s\"";
+ pFormatter->PutEntry(sS, nCheckPos, nType, nKeyS);
+ OUString sCM_S = "#\"cm/s\"";
+ pFormatter->PutEntry(sCM_S, nCheckPos, nType, nKeyCM_S);
+
+ // 1st column: 10cm, 20cm, 30cm
+ ScAddress address(0, 0, 0);
+ mpDoc->SetNumberFormat(address, nKeyCM);
+ mpDoc->SetValue(address, 10);
+
+ address.IncRow();
+ mpDoc->SetNumberFormat(address, nKeyCM);
+ mpDoc->SetValue(address, 20);
+
+ address.IncRow();
+ mpDoc->SetNumberFormat(address, nKeyCM);
+ mpDoc->SetValue(address, 30);
+
+ // 2nd column: 1kg, 2kg, 3kg
+ address = ScAddress(1, 0, 0);
+ mpDoc->SetNumberFormat(address, nKeyKG);
+ mpDoc->SetValue(address, 1);
+
+ address.IncRow();
+ mpDoc->SetNumberFormat(address, nKeyKG);
+ mpDoc->SetValue(address, 2);
+
+ address.IncRow();
+ mpDoc->SetNumberFormat(address, nKeyKG);
+ mpDoc->SetValue(address, 3);
+
+ // 3rd column: 1s, 2s, 3s
+ address = ScAddress(2, 0, 0);
+ mpDoc->SetNumberFormat(address, nKeyS);
+ mpDoc->SetValue(address, 1);
+
+ address.IncRow();
+ mpDoc->SetNumberFormat(address, nKeyS);
+ mpDoc->SetValue(address, 2);
+
+ address.IncRow();
+ mpDoc->SetNumberFormat(address, nKeyS);
+ mpDoc->SetValue(address, 3);
+
+ // 4th column: 5cm/s
+ address = ScAddress(3, 0, 0);
+ mpDoc->SetNumberFormat(address, nKeyCM_S);
+ mpDoc->SetValue(address, 5);
+
+ ScFormulaCell* pCell;
+ ScTokenArray* pTokens;
+
+ // Test that addition of the same unit is successful
+ address = ScAddress(0, 4, 0);
+ mpDoc->SetFormula(address, "=A1+A2");
+ pCell = mpDoc->GetFormulaCell(address);
+ pTokens = pCell->GetCode();
+ CPPUNIT_ASSERT(mpUnitsImpl->verifyFormula(pTokens, address, mpDoc));
+
+ // Test that addition of different units fails
+ address = ScAddress(0, 6, 0);
+ mpDoc->SetFormula(address, "=A1+B1");
+ pCell = mpDoc->GetFormulaCell(address);
+ pTokens = pCell->GetCode();
+ CPPUNIT_ASSERT(!mpUnitsImpl->verifyFormula(pTokens, address, mpDoc));
+
+ // Test that addition and multiplication works (i.e. kg*s+kg*s)
+ address = ScAddress(0, 7, 0);
+ mpDoc->SetFormula(address, "=A1*B1+A2*B2");
+ pCell = mpDoc->GetFormulaCell(address);
+ pTokens = pCell->GetCode();
+ CPPUNIT_ASSERT(mpUnitsImpl->verifyFormula(pTokens, address, mpDoc));
+
+ // Test another combination (i.e. cm/s+'cm/s')
+ address = ScAddress(0, 8, 0);
+ mpDoc->SetFormula(address, "=A1/C1+D1");
+ pCell = mpDoc->GetFormulaCell(address);
+ pTokens = pCell->GetCode();
+ CPPUNIT_ASSERT(mpUnitsImpl->verifyFormula(pTokens, address, mpDoc));
+
+ // Test that another combination fails (cm*kg/s+'cm/s')
+ address = ScAddress(0, 9, 0);
+ mpDoc->SetFormula(address, "=A1*B1/C1+D1");
+ pCell = mpDoc->GetFormulaCell(address);
+ pTokens = pCell->GetCode();
+ CPPUNIT_ASSERT(!mpUnitsImpl->verifyFormula(pTokens, address, mpDoc));
+}
+
CPPUNIT_TEST_SUITE_REGISTRATION(UnitsTest);
CPPUNIT_PLUGIN_IMPLEMENT();
commit 7150369e125a9ba870dc1cb0c30224fd40d5e47a
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon Jan 26 17:25:33 2015 +0000
No need to pass in unit system anymore.
getUnitForRef is now a member that can access our unit system
so we don't need to pass it in.
Change-Id: I31f7ebed98dda3e3573866c36ee84e631c8f6de8
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 13d2ed0..a2c5581 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -201,7 +201,7 @@ OUString UnitsImpl::extractUnitStringForCell(ScAddress& rAddress, ScDocument* pD
}
UtUnit UnitsImpl::getUnitForRef(FormulaToken* pToken, const ScAddress& rFormulaAddress,
- ScDocument* pDoc, ::boost::shared_ptr< ut_system > pUnitSystem) {
+ ScDocument* pDoc) {
assert(pToken->GetType() == formula::svSingleRef);
ScSingleRefData* pRef = pToken->GetSingleRef();
@@ -220,7 +220,7 @@ UtUnit UnitsImpl::getUnitForRef(FormulaToken* pToken, const ScAddress& rFormulaA
// hence we need to manually detect that case and return the dimensionless unit.
if (sUnitString.getLength() == 0) {
SAL_INFO("sc.units", "empty unit string: returning dimensionless unit");
- return UtUnit(ut_get_dimensionless_unit_one(pUnitSystem.get()));
+ return UtUnit(ut_get_dimensionless_unit_one(mpUnitSystem.get()));
}
SAL_INFO("sc.units", "got unit string [" << sUnitString << "]");
@@ -229,7 +229,7 @@ UtUnit UnitsImpl::getUnitForRef(FormulaToken* pToken, const ScAddress& rFormulaA
// TODO: we should probably have a cache of unit strings here to save reparsing
// on every run?
- UtUnit pUnit(ut_parse(pUnitSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8));
+ UtUnit pUnit(ut_parse(mpUnitSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8));
if (!pUnit) {
SAL_INFO("sc.units", "no unit obtained for token at cell " << aInputAddress.GetColRowString());
@@ -253,7 +253,7 @@ bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAdd
switch (pToken->GetType()) {
case formula::svSingleRef:
{
- UtUnit pUnit(getUnitForRef(pToken, rFormulaAddress, pDoc, mpUnitSystem));
+ UtUnit pUnit(getUnitForRef(pToken, rFormulaAddress, pDoc));
if (!pUnit) {
SAL_INFO("sc.units", "no unit returned for scSingleRef, ut_status: " << getUTStatus());
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index e8845a0..2561e1e 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -67,10 +67,8 @@ private:
OUString extractUnitStringFromFormat(const OUString& rFormatString);
OUString extractUnitStringForCell(ScAddress& rAddress, ScDocument* pDoc);
UtUnit getUnitForRef(formula::FormulaToken* pToken,
- const ScAddress& rFormulaAddress,
- ScDocument* pDoc,
- ::boost::shared_ptr< ut_system > pUnitSystem);
-
+ const ScAddress& rFormulaAddress,
+ ScDocument* pDoc);
};
}} // namespace sc::units
commit 2f94bdd3d2a6aa5617b2f8834f60ae3a347af0e5
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon Jan 26 17:21:50 2015 +0000
Implement setUp/tearDown for units test.
In preparation for adding more tests.
Change-Id: I63ba844d0b2b39df975abb6d4043e88fba9137dd
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index 6a7a08c..290e74a 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -28,6 +28,11 @@ public:
UnitsTest() {};
virtual ~UnitsTest() {};
+ virtual void setUp() SAL_OVERRIDE;
+ virtual void tearDown() SAL_OVERRIDE;
+
+ ::boost::shared_ptr< UnitsImpl > mpUnitsImpl;
+
void testStringExtraction();
CPPUNIT_TEST_SUITE(UnitsTest);
@@ -35,11 +40,19 @@ public:
CPPUNIT_TEST_SUITE_END();
};
-void UnitsTest::testStringExtraction() {
- ::boost::shared_ptr< UnitsImpl > pUnitsImpl = UnitsImpl::GetUnits();
+void UnitsTest::setUp() {
+ BootstrapFixture::setUp();
+
+ mpUnitsImpl = UnitsImpl::GetUnits();
+}
+
+void UnitsTest::tearDown() {
+ BootstrapFixture::tearDown();
+}
- CPPUNIT_ASSERT(pUnitsImpl->extractUnitStringFromFormat("\"weight: \"0.0\"kg\"") == "kg");
- CPPUNIT_ASSERT(pUnitsImpl->extractUnitStringFromFormat("#\"cm\"") == "cm");
+void UnitsTest::testStringExtraction() {
+ CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("\"weight: \"0.0\"kg\"") == "kg");
+ CPPUNIT_ASSERT(mpUnitsImpl->extractUnitStringFromFormat("#\"cm\"") == "cm");
}
CPPUNIT_TEST_SUITE_REGISTRATION(UnitsTest);
commit e4950c83318b5e9cc0bbf00e0f092ccc72effbae
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon Jan 26 17:13:53 2015 +0000
Add support for unary opcodes to unit verification.
Change-Id: I802079e6ee61a6807e97cd87df3b4bf3223d9b03
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index a1beed4..13d2ed0 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -81,7 +81,42 @@ UtUnit UnitsImpl::getOutputUnitsForOpCode(stack< UtUnit >& rUnitStack, const OpC
auto nOpCode = static_cast<std::underlying_type<const OpCode>::type>(rOpCode);
- if (nOpCode >= SC_OPCODE_START_BIN_OP &&
+ // TODO: sc/source/core/tool/parclass.cxx has a mapping of opcodes to possible operands, which we
+ // should probably be using in practice.
+
+ if (nOpCode >= SC_OPCODE_START_UN_OP &&
+ nOpCode < SC_OPCODE_STOP_UN_OP) {
+
+ if (!(rUnitStack.size() >= 1)) {
+ SAL_WARN("sc.units", "no units on stack for unary operation");
+ return 0;
+ }
+
+ UtUnit pUnit = rUnitStack.top();
+ rUnitStack.pop();
+
+ switch (rOpCode) {
+ case ocNot:
+ if (!ut_is_dimensionless(pUnit.get())) {
+ return 0;
+ }
+ // We just keep the same unit (in this case no unit) so can
+ // fall through.
+ case ocNeg:
+ // fall through -- same as OcNegSub
+ // It seems the difference is that ocNeg: 'NEG(value)', and ocNegSub: '-value' when
+ // in human readable form.
+ case ocNegSub:
+ // do nothing: since we're just negating the value which doesn't
+ // affect units in any way, we just return the current unit.
+ pOut = pUnit;
+ break;
+ default:
+ // Only the above 3 opcodes are in the range we have tested for previously
+ // (...START_UN_OP to ...STOP_UN_OP).
+ assert(false);
+ }
+ } else if (nOpCode >= SC_OPCODE_START_BIN_OP &&
nOpCode < SC_OPCODE_STOP_BIN_OP) {
if (!(rUnitStack.size() >= 2)) {
@@ -120,6 +155,8 @@ UtUnit UnitsImpl::getOutputUnitsForOpCode(stack< UtUnit >& rUnitStack, const OpC
assert(false);
}
+ } else {
+ SAL_INFO("sc.units", "unit verification not supported for opcode: " << nOpCode);
}
// TODO: else if unary, or no params, or ...
// TODO: implement further sensible opcode handling
commit 85715a8d9fbd4b15dcfc2c3f9af2ea33328b7657
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon Jan 26 16:41:29 2015 +0000
Allow access to whole stack for opcode processing.
An opcode can operate on 0, 1, 2 or n values/units, hence
we need to be able to access the whole stack in order to be
able to implement support for all opcodes.
Change-Id: I145d430ef412825ac4437a5049c955b2d7eb0bfb
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index 4c94df07..a1beed4 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -21,7 +21,6 @@
#include <svl/zformat.hxx>
#include <boost/scoped_array.hpp>
-#include <stack>
using namespace formula;
using namespace sc;
@@ -77,36 +76,55 @@ UnitsImpl::~UnitsImpl() {
// (i.e. if udunits can't handle being used across threads)?
}
-UtUnit UnitsImpl::getOutputUnitsForOpCode(const UtUnit& pFirstUnit, const UtUnit& pSecondUnit, const OpCode& rOpCode) {
+UtUnit UnitsImpl::getOutputUnitsForOpCode(stack< UtUnit >& rUnitStack, const OpCode& rOpCode) {
UtUnit pOut;
- switch (rOpCode) {
- case ocAdd:
- // Adding and subtracting both require the same units on both sides
- // hence we can just fall through / use the same logic.
- case ocSub:
- if (ut_compare(pFirstUnit.get(), pSecondUnit.get()) == 0) {
- // The two units are identical, hence we can return either.
- pOut = pFirstUnit;
- SAL_INFO("sc.units", "verified equality for unit " << pFirstUnit.getString());
- } else {
- // TODO: notify/link UI.
+ auto nOpCode = static_cast<std::underlying_type<const OpCode>::type>(rOpCode);
+
+ if (nOpCode >= SC_OPCODE_START_BIN_OP &&
+ nOpCode < SC_OPCODE_STOP_BIN_OP) {
+
+ if (!(rUnitStack.size() >= 2)) {
+ SAL_WARN("sc.units", "less than two units on stack when attempting binary operation");
+ // TODO: what should we be telling the user in this case? Can this even happen (i.e.
+ // should we just be asserting here?)
+ return 0;
+ }
+
+ UtUnit pSecondUnit = rUnitStack.top();
+ rUnitStack.pop();
+ UtUnit pFirstUnit = rUnitStack.top();
+ rUnitStack.pop();
+
+ switch (rOpCode) {
+ case ocAdd:
+ // Adding and subtracting both require the same units on both sides
+ // hence we can just fall through / use the same logic.
+ case ocSub:
+ if (ut_compare(pFirstUnit.get(), pSecondUnit.get()) == 0) {
+ // The two units are identical, hence we can return either.
+ pOut = pFirstUnit;
+ SAL_INFO("sc.units", "verified equality for unit " << pFirstUnit.getString());
+ } else {
+ // TODO: notify/link UI.
+ }
+ break;
+ case ocMul:
+ pOut.reset(ut_multiply(pFirstUnit.get(), pSecondUnit.get()));
+ break;
+ case ocDiv:
+ pOut.reset(ut_divide(pFirstUnit.get(), pSecondUnit.get()));
+ break;
+ default:
+ SAL_INFO("sc.units", "unit verification not supported for opcode: " << nOpCode);
+ assert(false);
}
- break;
- case ocMul:
- pOut.reset(ut_multiply(pFirstUnit.get(), pSecondUnit.get()));
- break;
- case ocDiv:
- pOut.reset(ut_divide(pFirstUnit.get(), pSecondUnit.get()));
- break;
- default:
- SAL_INFO("sc.units", "unit verification not supported for opcode: " << static_cast<std::underlying_type<const OpCode>::type>(rOpCode));
- assert(false);
+
}
+ // TODO: else if unary, or no params, or ...
+ // TODO: implement further sensible opcode handling
return pOut;
-
-// TODO: implement further sensible opcode handling
}
OUString UnitsImpl::extractUnitStringFromFormat(const OUString& rFormatString) {
@@ -216,17 +234,7 @@ bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAdd
}
case formula::svByte:
{
- if (!(aUnitStack.size() >= 2)) {
- SAL_WARN("sc.units", "less than two units on stack when attempting binary operation");
- return false;
- }
-
- UtUnit pSecondUnit = aUnitStack.top();
- aUnitStack.pop();
- UtUnit pFirstUnit = aUnitStack.top();
- aUnitStack.pop();
-
- UtUnit pOut = getOutputUnitsForOpCode(pFirstUnit, pSecondUnit, pToken->GetOpCode());
+ UtUnit pOut = getOutputUnitsForOpCode(aUnitStack, pToken->GetOpCode());
// A null unit indicates either invalid units and/or other erronous input
// i.e. is an indication that getOutputUnitsForOpCode failed.
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index 82a9b0a..e8845a0 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -23,6 +23,8 @@
#include <units.hxx>
#include "utunit.hxx"
+#include <stack>
+
namespace formula {
class FormulaToken;
}
@@ -61,7 +63,7 @@ public:
virtual bool verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAddress, ScDocument* pDoc) SAL_OVERRIDE;
private:
- UtUnit getOutputUnitsForOpCode(const UtUnit& pFirstUnit, const UtUnit& pSecondUnit, const OpCode& rOpCode);
+ UtUnit getOutputUnitsForOpCode(std::stack< UtUnit >& rUnitStack, const OpCode& rOpCode);
OUString extractUnitStringFromFormat(const OUString& rFormatString);
OUString extractUnitStringForCell(ScAddress& rAddress, ScDocument* pDoc);
UtUnit getUnitForRef(formula::FormulaToken* pToken,
commit 9bb2df441dfc975264cd8b31313521028fcdffec
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon Jan 26 16:25:26 2015 +0000
Update UnitsTest to use the refactored design.
Change-Id: I37c8d0c44612519e01e7c62353c395fbe4cf3116
diff --git a/sc/CppunitTest_sc_units.mk b/sc/CppunitTest_sc_units.mk
index 73fba99..241836b 100644
--- a/sc/CppunitTest_sc_units.mk
+++ b/sc/CppunitTest_sc_units.mk
@@ -78,6 +78,7 @@ $(eval $(call gb_CppunitTest_set_include,sc_units,\
-I$(SRCDIR)/sc/source/ui/inc \
-I$(SRCDIR)/sc/source/core/inc \
-I$(SRCDIR)/sc/inc \
+ -I$(SRCDIR)/sc/source/core/units \
$$(INCLUDE) \
))
diff --git a/sc/qa/unit/units.cxx b/sc/qa/unit/units.cxx
index fb091e4..6a7a08c 100644
--- a/sc/qa/unit/units.cxx
+++ b/sc/qa/unit/units.cxx
@@ -7,12 +7,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-#include "units.hxx"
+#include "unitsimpl.hxx"
#include "helper/qahelper.hxx"
+using namespace sc::units;
+
+// In order to be able to access the private members of UnitsImpl for
+// testing, we need to be a friend of UnitsImpl. For this to work
+// UnitsTest can't be a member of the anonymous namespace hence the
+// need to use a namespace here.
+namespace sc {
+namespace units {
+namespace test {
+
class UnitsTest:
- public test::BootstrapFixture
+ public ::test::BootstrapFixture
{
public:
UnitsTest() {};
@@ -26,12 +36,16 @@ public:
};
void UnitsTest::testStringExtraction() {
- CPPUNIT_ASSERT(extractUnitStringFromFormat("\"weight: \"0.0\"kg\"") == "kg");
- CPPUNIT_ASSERT(extractUnitStringFromFormat("#\"cm\"") == "cm");
+ ::boost::shared_ptr< UnitsImpl > pUnitsImpl = UnitsImpl::GetUnits();
+
+ CPPUNIT_ASSERT(pUnitsImpl->extractUnitStringFromFormat("\"weight: \"0.0\"kg\"") == "kg");
+ CPPUNIT_ASSERT(pUnitsImpl->extractUnitStringFromFormat("#\"cm\"") == "cm");
}
CPPUNIT_TEST_SUITE_REGISTRATION(UnitsTest);
CPPUNIT_PLUGIN_IMPLEMENT();
+}}} // namespace sc::units::test
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/units.hxx b/sc/qa/unit/units.hxx
deleted file mode 100644
index 16861a96..0000000
--- a/sc/qa/unit/units.hxx
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#ifndef INCLUDED_SC_QA_UNIT_UNITS_HXX
-#define INCLUDED_SC_QA_UNIT_UNITS_HXX
-
-#include <rtl/ustring.hxx>
-
-// Forward declarations of our local functions that need testing
-// here.
-OUString extractUnitStringFromFormat(const OUString& rFormatString);
-
-#endif
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
index af8d679..4c94df07 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -7,7 +7,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
-#include "unitsImpl.hxx"
+#include "unitsimpl.hxx"
#include "util.hxx"
@@ -31,7 +31,7 @@ using namespace std;
::osl::Mutex sc::units::UnitsImpl::ourSingletonMutex;
::boost::weak_ptr< UnitsImpl > sc::units::UnitsImpl::ourUnits;
-::boost::shared_ptr< Units > UnitsImpl::GetUnits() {
+::boost::shared_ptr< UnitsImpl > UnitsImpl::GetUnits() {
osl::MutexGuard aGuard(ourSingletonMutex);
boost::shared_ptr< UnitsImpl > pUnits = ourUnits.lock();
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
index 7ea1700..82a9b0a 100644
--- a/sc/source/core/units/unitsimpl.hxx
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -32,7 +32,13 @@ struct ut_system;
namespace sc {
namespace units {
+namespace test {
+ class UnitsTest;
+}
+
class UnitsImpl: public Units {
+ friend class test::UnitsTest;
+
private:
static ::osl::Mutex ourSingletonMutex;
static ::boost::weak_ptr< UnitsImpl > ourUnits;
@@ -47,7 +53,7 @@ private:
}
public:
- static ::boost::shared_ptr< Units > GetUnits();
+ static ::boost::shared_ptr< UnitsImpl > GetUnits();
UnitsImpl();
virtual ~UnitsImpl();
commit 9cb0923b0112986bf486063740d76ffb895a93a0
Author: Andrzej Hunt <andrzej at ahunt.org>
Date: Mon Jan 26 15:52:30 2015 +0000
Split up / refactor unit verification.
Change-Id: Ic188cf4046ed6d3f1e705f7fe97a26a7914ab4d8
diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index 4fcc25b..778415c 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -689,6 +689,9 @@ $(call gb_Library_add_exception_objects,sc,\
ifeq ($(ENABLE_CALC_UNITVERIFICATION),TRUE)
$(eval $(call gb_Library_add_exception_objects,sc,\
sc/source/core/units/units \
+ sc/source/core/units/unitsimpl \
+ sc/source/core/units/util \
+ sc/source/core/units/utunit \
))
endif
diff --git a/sc/inc/units.hxx b/sc/inc/units.hxx
index d3aec50..cfeae9e 100644
--- a/sc/inc/units.hxx
+++ b/sc/inc/units.hxx
@@ -11,45 +11,26 @@
#define INCLUDED_SC_INC_UNITS_HXX
#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-#include <osl/mutex.hxx>
-
-struct ut_system;
class ScAddress;
class ScDocument;
class ScTokenArray;
namespace sc {
+namespace units {
-/*
- * We implement this as a singleton which automatically
- * cleans itself up thanks to the use of shared and weak
- * pointers.
- */
-class Units {
-private:
- // A scoped_ptr would be more appropriate, however
- // we require a custom deleter which scoped_ptr doesn't
- // offer.
- ::boost::shared_ptr< ut_system > mpUnitSystem;
-
- Units();
-
- static ::osl::Mutex ourSingletonMutex;
- static ::boost::weak_ptr< Units > ourUnits;
+class UnitsImpl;
+class Units {
public:
static ::boost::shared_ptr< Units > GetUnits();
- ~Units();
-
- bool verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAddress, ScDocument* pDoc);
+ virtual bool verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAddress, ScDocument* pDoc) = 0;
+ virtual ~Units() {}
};
-} // namespace sc
+}} // namespace sc::units
#endif // INCLUDED_SC_INC_UNITS_HXX
diff --git a/sc/source/core/units/units.cxx b/sc/source/core/units/units.cxx
index f7b12b9..190aa96 100644
--- a/sc/source/core/units/units.cxx
+++ b/sc/source/core/units/units.cxx
@@ -7,335 +7,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
-#include "units.hxx"
+#include <units.hxx>
+#include "unitsimpl.hxx"
-#include "document.hxx"
-#include "refdata.hxx"
-#include "tokenarray.hxx"
-
-#include <osl/file.hxx>
-#include <osl/mutex.hxx>
-#include <rtl/bootstrap.hxx>
-#include <svl/zformat.hxx>
-
-#include <boost/scoped_array.hpp>
-#include <stack>
-
-#include <udunits2.h>
-
-using namespace formula;
using namespace sc;
-using namespace std;
-
-::osl::Mutex sc::Units::ourSingletonMutex;
-::boost::weak_ptr< Units > sc::Units::ourUnits;
-
-OUString dumpUTStatus() {
- switch(ut_get_status()) {
- case UT_SUCCESS:
- return "UT_SUCCESS: successful!";
- case UT_BAD_ARG:
- return "UT_BAD_ARG: invalid argument";
- case UT_EXISTS:
- return "UT_EXISTS: unit/prefix/identifier already exists";
- case UT_NO_UNIT:
- return "UT_NO_UNIT: no such unit exists";
- case UT_OS:
- return "UT_OS: operating system error (check errno?)";
- case UT_NOT_SAME_SYSTEM:
- return "UT_NOT_SAME_SYSTEM: units not in same unit system";
- case UT_MEANINGLESS:
- return "UT_MEANINGLESS: operation is meaningless";
- case UT_NO_SECOND:
- return "UT_NO_SECOND: no unit named second";
- case UT_VISIT_ERROR:
- return "UT_VISIT_ERROR";
- case UT_CANT_FORMAT:
- return "UT_CANT_FORMAT";
- case UT_SYNTAX:
- return "UT_SYNTAX: syntax error in unit string";
- case UT_UNKNOWN:
- return "UT_UNKNOWN: unknown unit encountered";
- case UT_OPEN_ARG:
- return "UT_OPEN_ARG: can't open specified unit database (arg)";
- case UT_OPEN_ENV:
- return "UT_OPEN_ENV: can't open specified unit databse (env)";
- case UT_OPEN_DEFAULT:
- return "UT_OPEN_DEFAULT: can't open default unit database";
- default:
- return "other (unpspecified) error encountered";
- }
-}
-
-class UnitP:
- public ::boost::shared_ptr< ut_unit > {
-public:
- UnitP(ut_unit* pUnit):
- boost::shared_ptr< ut_unit >(pUnit, &freeUt)
- {}
-
- UnitP():
- boost::shared_ptr< ut_unit >(0, &freeUt)
- {}
-
- void reset(ut_unit* pUnit) {
- boost::shared_ptr< ut_unit >::reset(pUnit, &freeUt);
- }
-
- OUString getString() const {
- char aBuf[200];
- int nChars = ut_format(this->get(), aBuf, 200, UT_UTF8);
- if (nChars == -1) {
- SAL_INFO("sc.units", "couldn't format unit: " << dumpUTStatus());
- // Placeholder for unformattable strings.
- return "?";
- }
-
- // If the output doesn't fit in the buffer, ut_format doesn't write
- // a terminating null. However for any output we have the correct length
- // as returned by ut_format, which is the easiest way to ensure the OString
- // constructor will always work correctly. (Alternatively we could retry with
- // a larger buffer, however this method is purely for debugging purposes for now.)
-
- return OUString(aBuf, nChars, RTL_TEXTENCODING_UTF8);
- }
-
-private:
- static void freeUt(ut_unit* pUnit) {
- ut_free(pUnit);
- }
-};
-
-UnitP getOutputUnitsForOpCode(const UnitP& pFirstUnit, const UnitP& pSecondUnit, const OpCode& rOpCode) {
- UnitP pOut;
-
- switch (rOpCode) {
- case ocAdd:
- // Adding and subtracting both require the same units on both sides
- // hence we can just fall through / use the same logic.
- case ocSub:
- if (ut_compare(pFirstUnit.get(), pSecondUnit.get()) == 0) {
- // The two units are identical, hence we can return either.
- pOut = pFirstUnit;
- SAL_INFO("sc.units", "verified equality for unit " << pFirstUnit.getString());
- } else {
- // TODO: notify/link UI.
- }
- break;
- case ocMul:
- pOut.reset(ut_multiply(pFirstUnit.get(), pSecondUnit.get()));
- break;
- case ocDiv:
- pOut.reset(ut_divide(pFirstUnit.get(), pSecondUnit.get()));
- break;
- default:
- SAL_INFO("sc.units", "unit verification not supported for opcode: " << static_cast<std::underlying_type<const OpCode>::type>(rOpCode));
- assert(false);
- }
-
- return pOut;
-
-// TODO: implement further sensible opcode handling
-}
-
-static void freeUtSystem(ut_system* pSystem) {
- ut_free_system(pSystem);
-}
-
-Units::Units() {
- SAL_INFO("sc.units", "initialising udunits2");
-
- // System udunits will (/should) be able to find it's unit database
- // itself -- however for bundled udunits we always need to find the
- // correct relative path within our LO installation.
-#ifdef USING_SYSTEM_UDUNITS
- const sal_Char* pPath = 0;
-#else
- OUString sDBURL("$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/udunits2/udunits2.xml");
- ::rtl::Bootstrap::expandMacros(sDBURL);
- OUString sDBPath;
- ::osl::FileBase::getSystemPathFromFileURL(sDBURL, sDBPath);
-
- OString sDBPathOut = OUStringToOString(sDBPath, RTL_TEXTENCODING_ASCII_US);
- const sal_Char* pPath = sDBPathOut.getStr();
-#endif
-
- mpUnitSystem = boost::shared_ptr< ut_system >( ut_read_xml( pPath ),
- &freeUtSystem );
-
- SAL_INFO("sc.units", "udunits2 initialised");
-}
-
-Units::~Units() {
- // We only arrive here if all shared_ptr's to our Units get
- // disposed. In this case the weak_ptr is already cleared,
- // and any new calls to GetUnits don't need to care at what
- // stage of destruction we are?
-
- // We might need to lock on our singletonMutex if we can't
- // load the same unit system multiple times in memory
- // (i.e. if udunits can't handle being used across threads)?
-}
+using namespace sc::units;
boost::shared_ptr< Units > Units::GetUnits() {
- osl::MutexGuard aGuard(ourSingletonMutex);
- boost::shared_ptr< Units > pUnits = ourUnits.lock();
-
- if (!pUnits) {
- pUnits.reset( new Units() );
- ourUnits = pUnits;
- }
- return pUnits;
-}
-
-OUString extractUnitStringFromFormat(const OUString& rFormatString) {
- // TODO: decide what we do for different subformats? Simplest solution
- // would be to not allow unit storage for multiple subformats.
- // TODO: we should check the number of subformats here in future?
-
- // TODO: use proper string processing routines?
-
- sal_Int32 nPos = rFormatString.getLength() - 1;
-
- // Only iterate if we have a string item at the end of our format string
- if (rFormatString[nPos] == '\"') {
- // TODO: deal with escaped strings? (Does that exist in these?)
- while (rFormatString[--nPos] != '\"') {
- if (nPos == 0) {
- // TODO: plug into our error reporting here to return bad escaping?
- return "";
- }
- }
- } else { // otherwise we have no units for this cell
- return "";
- }
-
- // Ensure that the parentheses are NOT included in our unit string.
- return rFormatString.copy(nPos + 1, rFormatString.getLength() - nPos - 2);
-}
-
-
-OUString extractUnitStringForCell(ScAddress& rAddress, ScDocument* pDoc) {
- sal_uInt32 nFormat = pDoc->GetNumberFormat(rAddress);
- const SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
- const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat );
- const OUString& rFormatString = pFormat->GetFormatstring();
-
- return extractUnitStringFromFormat(rFormatString);
-}
-
-// get units for single ref -- use format, but then fall back to header?
-// or have some sort of marker per cell? (Per cell range -- linked to mdds?)
-
-UnitP getUnitForRef(FormulaToken* pToken, const ScAddress& rFormulaAddress,
- ScDocument* pDoc, ::boost::shared_ptr< ut_system > pUnitSystem) {
- assert(pToken->GetType() == formula::svSingleRef);
-
- ScSingleRefData* pRef = pToken->GetSingleRef();
- assert(pRef);
-
- // Addresses can/will be relative to the formula, for extracting
- // units however we will need to get the absolute address (i.e.
- // by adding the current address to the relative formula address).
- ScAddress aInputAddress = pRef->toAbs( rFormulaAddress );
-
- // udunits requires strings to be trimmed before parsing -- it's easiest to do this
- // using the OUString utils (as opposed to using ut_trim once we have a c string.
- OUString sUnitString = extractUnitStringForCell(aInputAddress, pDoc).trim();
-
- // empty string == dimensionless unit. ut_parse returns an error for an empty string
- // hence we need to manually detect that case and return the dimensionless unit.
- if (sUnitString.getLength() == 0) {
- SAL_INFO("sc.units", "empty unit string: returning dimensionless unit");
- return UnitP(ut_get_dimensionless_unit_one(pUnitSystem.get()));
- }
-
- SAL_INFO("sc.units", "got unit string [" << sUnitString << "]");
- OString sUnitStringUTF8 = OUStringToOString(sUnitString, RTL_TEXTENCODING_UTF8);
-
- // TODO: we should probably have a cache of unit strings here to save reparsing
- // on every run?
-
- UnitP pUnit(ut_parse(pUnitSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8));
-
- if (!pUnit) {
- SAL_INFO("sc.units", "no unit obtained for token at cell " << aInputAddress.GetColRowString());
- SAL_INFO("sc.units", "error encountered: " << dumpUTStatus());
- }
-
- return pUnit;
+ return UnitsImpl::GetUnits();
}
-// getUnitForRef: check format -> if not in format, use more complicated method? (Format overrides header definition)
-
-bool Units::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAddress, ScDocument* pDoc) {
-#if DEBUG_FORMULA_COMPILER
- pArray->Dump();
-#endif
-
- stack< UnitP > aUnitStack;
-
- FormulaToken* pToken = pArray->FirstRPN();
-
- while (pToken != 0) {
- switch (pToken->GetType()) {
- case formula::svSingleRef:
- {
- UnitP pUnit(getUnitForRef(pToken, rFormulaAddress, pDoc, mpUnitSystem));
-
- if (!pUnit) {
- SAL_INFO("sc.units", "no unit returned for scSingleRef, ut_status: " << dumpUTStatus());
-
- // This only happens in case of parsing (or system) errors.
- // However maybe we should be returning "unverified" for
- // unparseable formulas?
- // (or even have a "can't be verified" state too?)
- // see below for more.
- return false;
- }
-
- aUnitStack.push(pUnit);
- break;
- }
- case formula::svByte:
- {
- if (!(aUnitStack.size() >= 2)) {
- SAL_WARN("sc.units", "less than two units on stack when attempting binary operation");
- return false;
- }
-
- UnitP pSecondUnit = aUnitStack.top();
- aUnitStack.pop();
- UnitP pFirstUnit = aUnitStack.top();
- aUnitStack.pop();
-
- UnitP pOut = getOutputUnitsForOpCode(pFirstUnit, pSecondUnit, pToken->GetOpCode());
-
- // A null unit indicates either invalid units and/or other erronous input
- // i.e. is an indication that getOutputUnitsForOpCode failed.
- if (pOut) {
- aUnitStack.push(pOut);
- } else {
- return false;
- }
-
- break;
- }
- default:
- // We can't parse any other types of tokens yet, so assume that the formula
- // was correct.
- // TODO: maybe we should have a "unverified" return state instead?
- SAL_WARN("sc.units", "Unrecognised token type " << pToken->GetType());
- return true;
- }
-
- pToken = pArray->NextRPN();
- }
-
- // TODO: only fail if actual parsing fails?
-
- return true;
-}
-
-
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/units/unitsimpl.cxx b/sc/source/core/units/unitsimpl.cxx
new file mode 100644
index 0000000..af8d679
--- /dev/null
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+#include "unitsImpl.hxx"
+
+#include "util.hxx"
+
+#include "document.hxx"
+#include "refdata.hxx"
+#include "tokenarray.hxx"
+
+#include <osl/file.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/bootstrap.hxx>
+#include <svl/zformat.hxx>
+
+#include <boost/scoped_array.hpp>
+#include <stack>
+
+using namespace formula;
+using namespace sc;
+using namespace sc::units;
+using namespace std;
+
+::osl::Mutex sc::units::UnitsImpl::ourSingletonMutex;
+::boost::weak_ptr< UnitsImpl > sc::units::UnitsImpl::ourUnits;
+
+::boost::shared_ptr< Units > UnitsImpl::GetUnits() {
+ osl::MutexGuard aGuard(ourSingletonMutex);
+ boost::shared_ptr< UnitsImpl > pUnits = ourUnits.lock();
+
+ if (!pUnits) {
+ pUnits.reset( new UnitsImpl() );
+ ourUnits = pUnits;
+ }
+ return pUnits;
+}
+
+UnitsImpl::UnitsImpl() {
+ SAL_INFO("sc.units", "initialising udunits2");
+
+ // System udunits will (/should) be able to find it's unit database
+ // itself -- however for bundled udunits we always need to find the
+ // correct relative path within our LO installation.
+#ifdef USING_SYSTEM_UDUNITS
+ const sal_Char* pPath = 0;
+#else
+ OUString sDBURL("$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/udunits2/udunits2.xml");
+ ::rtl::Bootstrap::expandMacros(sDBURL);
+ OUString sDBPath;
+ ::osl::FileBase::getSystemPathFromFileURL(sDBURL, sDBPath);
+
+ OString sDBPathOut = OUStringToOString(sDBPath, RTL_TEXTENCODING_ASCII_US);
+ const sal_Char* pPath = sDBPathOut.getStr();
+#endif
+
+ mpUnitSystem = boost::shared_ptr< ut_system >( ut_read_xml( pPath ),
+ &freeUtSystem );
+
+ SAL_INFO("sc.units", "udunits2 initialised");
+}
+
+UnitsImpl::~UnitsImpl() {
+ // We only arrive here if all shared_ptr's to our Units get
+ // disposed. In this case the weak_ptr is already cleared,
+ // and any new calls to GetUnits don't need to care at what
+ // stage of destruction we are?
+
+ // We might need to lock on our singletonMutex if we can't
+ // load the same unit system multiple times in memory
+ // (i.e. if udunits can't handle being used across threads)?
+}
+
+UtUnit UnitsImpl::getOutputUnitsForOpCode(const UtUnit& pFirstUnit, const UtUnit& pSecondUnit, const OpCode& rOpCode) {
+ UtUnit pOut;
+
+ switch (rOpCode) {
+ case ocAdd:
+ // Adding and subtracting both require the same units on both sides
+ // hence we can just fall through / use the same logic.
+ case ocSub:
+ if (ut_compare(pFirstUnit.get(), pSecondUnit.get()) == 0) {
+ // The two units are identical, hence we can return either.
+ pOut = pFirstUnit;
+ SAL_INFO("sc.units", "verified equality for unit " << pFirstUnit.getString());
+ } else {
+ // TODO: notify/link UI.
+ }
+ break;
+ case ocMul:
+ pOut.reset(ut_multiply(pFirstUnit.get(), pSecondUnit.get()));
+ break;
+ case ocDiv:
+ pOut.reset(ut_divide(pFirstUnit.get(), pSecondUnit.get()));
+ break;
+ default:
+ SAL_INFO("sc.units", "unit verification not supported for opcode: " << static_cast<std::underlying_type<const OpCode>::type>(rOpCode));
+ assert(false);
+ }
+
+ return pOut;
+
+// TODO: implement further sensible opcode handling
+}
+
+OUString UnitsImpl::extractUnitStringFromFormat(const OUString& rFormatString) {
+ // TODO: decide what we do for different subformats? Simplest solution
+ // would be to not allow unit storage for multiple subformats.
+ // TODO: we should check the number of subformats here in future?
+
+ // TODO: use proper string processing routines?
+
+ sal_Int32 nPos = rFormatString.getLength() - 1;
+
+ // Only iterate if we have a string item at the end of our format string
+ if (rFormatString[nPos] == '\"') {
+ // TODO: deal with escaped strings? (Does that exist in these?)
+ while (rFormatString[--nPos] != '\"') {
+ if (nPos == 0) {
+ // TODO: plug into our error reporting here to return bad escaping?
+ return "";
+ }
+ }
+ } else { // otherwise we have no units for this cell
+ return "";
+ }
+
+ // Ensure that the parentheses are NOT included in our unit string.
+ return rFormatString.copy(nPos + 1, rFormatString.getLength() - nPos - 2);
+}
+
+
+OUString UnitsImpl::extractUnitStringForCell(ScAddress& rAddress, ScDocument* pDoc) {
+ sal_uInt32 nFormat = pDoc->GetNumberFormat(rAddress);
+ const SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
+ const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat );
+ const OUString& rFormatString = pFormat->GetFormatstring();
+
+ return extractUnitStringFromFormat(rFormatString);
+}
+
+UtUnit UnitsImpl::getUnitForRef(FormulaToken* pToken, const ScAddress& rFormulaAddress,
+ ScDocument* pDoc, ::boost::shared_ptr< ut_system > pUnitSystem) {
+ assert(pToken->GetType() == formula::svSingleRef);
+
+ ScSingleRefData* pRef = pToken->GetSingleRef();
+ assert(pRef);
+
+ // Addresses can/will be relative to the formula, for extracting
+ // units however we will need to get the absolute address (i.e.
+ // by adding the current address to the relative formula address).
+ ScAddress aInputAddress = pRef->toAbs( rFormulaAddress );
+
+ // udunits requires strings to be trimmed before parsing -- it's easiest to do this
+ // using the OUString utils (as opposed to using ut_trim once we have a c string.
+ OUString sUnitString = extractUnitStringForCell(aInputAddress, pDoc).trim();
+
+ // empty string == dimensionless unit. ut_parse returns an error for an empty string
+ // hence we need to manually detect that case and return the dimensionless unit.
+ if (sUnitString.getLength() == 0) {
+ SAL_INFO("sc.units", "empty unit string: returning dimensionless unit");
+ return UtUnit(ut_get_dimensionless_unit_one(pUnitSystem.get()));
+ }
+
+ SAL_INFO("sc.units", "got unit string [" << sUnitString << "]");
+ OString sUnitStringUTF8 = OUStringToOString(sUnitString, RTL_TEXTENCODING_UTF8);
+
+ // TODO: we should probably have a cache of unit strings here to save reparsing
+ // on every run?
+
+ UtUnit pUnit(ut_parse(pUnitSystem.get(), sUnitStringUTF8.getStr(), UT_UTF8));
+
+ if (!pUnit) {
+ SAL_INFO("sc.units", "no unit obtained for token at cell " << aInputAddress.GetColRowString());
+ SAL_INFO("sc.units", "error encountered: " << getUTStatus());
+ }
+
+ return pUnit;
+}
+
+// getUnitForRef: check format -> if not in format, use more complicated method? (Format overrides header definition)
+bool UnitsImpl::verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAddress, ScDocument* pDoc) {
+#if DEBUG_FORMULA_COMPILER
+ pArray->Dump();
+#endif
+
+ stack< UtUnit > aUnitStack;
+
+ FormulaToken* pToken = pArray->FirstRPN();
+
+ while (pToken != 0) {
+ switch (pToken->GetType()) {
+ case formula::svSingleRef:
+ {
+ UtUnit pUnit(getUnitForRef(pToken, rFormulaAddress, pDoc, mpUnitSystem));
+
+ if (!pUnit) {
+ SAL_INFO("sc.units", "no unit returned for scSingleRef, ut_status: " << getUTStatus());
+
+ // This only happens in case of parsing (or system) errors.
+ // However maybe we should be returning "unverified" for
+ // unparseable formulas?
+ // (or even have a "can't be verified" state too?)
+ // see below for more.
+ return false;
+ }
+
+ aUnitStack.push(pUnit);
+ break;
+ }
+ case formula::svByte:
+ {
+ if (!(aUnitStack.size() >= 2)) {
+ SAL_WARN("sc.units", "less than two units on stack when attempting binary operation");
+ return false;
+ }
+
+ UtUnit pSecondUnit = aUnitStack.top();
+ aUnitStack.pop();
+ UtUnit pFirstUnit = aUnitStack.top();
+ aUnitStack.pop();
+
+ UtUnit pOut = getOutputUnitsForOpCode(pFirstUnit, pSecondUnit, pToken->GetOpCode());
+
+ // A null unit indicates either invalid units and/or other erronous input
+ // i.e. is an indication that getOutputUnitsForOpCode failed.
+ if (pOut) {
+ aUnitStack.push(pOut);
+ } else {
+ return false;
+ }
+
+ break;
+ }
+ default:
+ // We can't parse any other types of tokens yet, so assume that the formula
+ // was correct.
+ // TODO: maybe we should have a "unverified" return state instead?
+ SAL_WARN("sc.units", "Unrecognised token type " << pToken->GetType());
+ return true;
+ }
+
+ pToken = pArray->NextRPN();
+ }
+
+ // TODO: only fail if actual parsing fails?
+
+ return true;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/units/unitsimpl.hxx b/sc/source/core/units/unitsimpl.hxx
new file mode 100644
index 0000000..7ea1700
--- /dev/null
+++ b/sc/source/core/units/unitsimpl.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+#ifndef INCLUDED_SC_SOURCE_CORE_UNITS_UNITSIMPL_HXX
+#define INCLUDED_SC_SOURCE_CORE_UNITS_UNITSIMPL_HXX
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <formula/opcode.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+
+#include <udunits2.h>
+
+#include <units.hxx>
+#include "utunit.hxx"
+
+namespace formula {
+ class FormulaToken;
+}
+
+struct ut_system;
+
+namespace sc {
+namespace units {
+
+class UnitsImpl: public Units {
+private:
+ static ::osl::Mutex ourSingletonMutex;
+ static ::boost::weak_ptr< UnitsImpl > ourUnits;
+
+ // A scoped_ptr would be more appropriate, however
+ // we require a custom deleter which scoped_ptr doesn't
+ // offer.
+ ::boost::shared_ptr< ut_system > mpUnitSystem;
+
+ static void freeUtSystem(ut_system* pSystem) {
+ ut_free_system(pSystem);
+ }
+
+public:
+ static ::boost::shared_ptr< Units > GetUnits();
+
+ UnitsImpl();
+ virtual ~UnitsImpl();
+
+ virtual bool verifyFormula(ScTokenArray* pArray, const ScAddress& rFormulaAddress, ScDocument* pDoc) SAL_OVERRIDE;
+
+private:
+ UtUnit getOutputUnitsForOpCode(const UtUnit& pFirstUnit, const UtUnit& pSecondUnit, const OpCode& rOpCode);
+ OUString extractUnitStringFromFormat(const OUString& rFormatString);
+ OUString extractUnitStringForCell(ScAddress& rAddress, ScDocument* pDoc);
+ UtUnit getUnitForRef(formula::FormulaToken* pToken,
+ const ScAddress& rFormulaAddress,
+ ScDocument* pDoc,
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list