[Libreoffice-commits] core.git: Branch 'feature/unitver' - 8 commits - sc/inc sc/qa sc/source

Andrzej Hunt andrzej at ahunt.org
Fri Feb 6 10:49:21 PST 2015


 sc/inc/units.hxx                   |   12 +++++
 sc/qa/unit/units.cxx               |   86 ++++++++++++++++++++++++++++++++++---
 sc/source/core/units/unitsimpl.cxx |   74 +++++++++++++++++++------------
 sc/source/core/units/unitsimpl.hxx |    1 
 sc/source/core/units/utunit.cxx    |   17 +++++++
 sc/source/core/units/utunit.hxx    |   46 +++++++++++++++----
 6 files changed, 193 insertions(+), 43 deletions(-)

New commits:
commit 683eb1b54412c2ceaafd46c607606a5b6b3e27af
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 2ca85e1..34eccde 100644
--- a/sc/source/core/units/unitsimpl.cxx
+++ b/sc/source/core/units/unitsimpl.cxx
@@ -285,5 +285,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 e68cf2b5b1dbde20b8ae89de8d19e1c0dd752a83
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 ea4fc7a418039abc73831cb7fa7f83da4d62c79f
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 0adbd28..2ca85e1 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 c47230f8927ecd4c431dad3909a86cfebff88ea2
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 3931221f867ef781fc162a2ad9130fee5c66b44f
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 c2d4721..0adbd28 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();
@@ -215,31 +215,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)
@@ -258,7 +241,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.
@@ -278,7 +261,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 70917fb99fdcf5bf9d7f93dd74443b5e151beecf
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 fb77de002cc5e52b9d38eca031cef0f3657f26a6
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 db2c8607dd8111e81f0d673a66dcd07b41e0b467
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)
     {}


More information about the Libreoffice-commits mailing list