[Libreoffice-commits] core.git: Branch 'libreoffice-4-2' - sc/source

Eike Rathke erack at redhat.com
Thu Apr 10 20:52:59 PDT 2014


 sc/source/core/data/column3.cxx  |  108 ++++++++++++++++++++++++++++--------
 sc/source/ui/docshell/docsh8.cxx |  116 ++++++++++++++++++++++++++-------------
 2 files changed, 165 insertions(+), 59 deletions(-)

New commits:
commit c73c6b24e64b0fbfb7feab62966ba967fbad8411
Author: Eike Rathke <erack at redhat.com>
Date:   Mon Mar 31 19:28:31 2014 +0200

    re-enabled user-defined numeric fields for dBase export
    
    Since commit f59e350d1733125055f1144f8b3b1b0a46f6d1ca it was impossible
    to define a numeric field with a precision of less than 2 decimals, even
    if all values were integers. It was also impossible to define a field
    width larger than needed for any values in that column. Furthermore, the
    integer part was shortened if the overall column's values resulted in
    more precision than defined, but the overall length did not reach the
    predefined length.
    
    This does not change the behavior of the original intention of
    f59e350d1733125055f1144f8b3b1b0a46f6d1ca to give the precision of number
    formats precedence over precision defined in the column header, which is
    debatable though because conflicts may silently change the field
    definition.
    
    (cherry picked from commit e65141e93a540fc9fb4343ee65a5a7da7e3b1769)
    
    Plus comment translation.
    
    Conflicts:
    	sc/source/ui/docshell/docsh8.cxx
    
    Change-Id: I234c4bceaa1a6aadbd259cb8d9b6cb6f16bf91c2
    Reviewed-on: https://gerrit.libreoffice.org/8806
    Reviewed-by: Kohei Yoshida <libreoffice at kohei.us>
    Tested-by: Kohei Yoshida <libreoffice at kohei.us>

diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index d47893f..192efcb 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -2710,25 +2710,42 @@ class MaxNumStringLenHandler
     SvNumberFormatter* mpFormatter;
     sal_Int32 mnMaxLen;
     sal_uInt16 mnPrecision;
+    sal_uInt16 mnMaxGeneralPrecision;
+    bool mbHaveSigned;
 
     void processCell(size_t nRow, ScRefCellValue& rCell)
     {
-        if (rCell.meType == CELLTYPE_FORMULA && !rCell.mpFormula->IsValue())
-            return;
+        sal_uInt16 nCellPrecision = mnMaxGeneralPrecision;
+        if (rCell.meType == CELLTYPE_FORMULA)
+        {
+            if (!rCell.mpFormula->IsValue())
+                return;
+
+            // Limit unformatted formula cell precision to precision
+            // encountered so far, if any, otherwise we'd end up with 15 just
+            // because of =1/3 ...  If no precision yet then arbitrarily limit
+            // to a maximum of 4 unless a maximum general precision is set.
+            if (mnPrecision)
+                nCellPrecision = mnPrecision;
+            else
+                nCellPrecision = (mnMaxGeneralPrecision >= 15) ? 4 : mnMaxGeneralPrecision;
+        }
+
+        double fVal = rCell.getValue();
+        if (!mbHaveSigned && fVal < 0.0)
+            mbHaveSigned = true;
 
         OUString aString;
+        OUString aSep;
+        sal_Int32 nLen;
+        sal_uInt16 nPrec;
         sal_uInt32 nFormat = static_cast<const SfxUInt32Item*>(
-            mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT))->GetValue();
-        ScCellFormat::GetInputString(rCell, nFormat, aString, *mpFormatter, &mrColumn.GetDoc());
-        sal_Int32 nLen = aString.getLength();
-        if (nLen <= 0)
-            // Ignore empty string.
-            return;
-
-        if (nFormat)
+                mrColumn.GetAttr(nRow, ATTR_VALUE_FORMAT))->GetValue();
+        if (nFormat % SV_COUNTRY_LANGUAGE_OFFSET)
         {
+            aSep = mpFormatter->GetFormatDecimalSep(nFormat);
+            ScCellFormat::GetInputString(rCell, nFormat, aString, *mpFormatter, &mrColumn.GetDoc());
             const SvNumberformat* pEntry = mpFormatter->GetEntry(nFormat);
-            sal_uInt16 nPrec;
             if (pEntry)
             {
                 bool bThousand, bNegRed;
@@ -2737,15 +2754,54 @@ class MaxNumStringLenHandler
             }
             else
                 nPrec = mpFormatter->GetFormatPrecision(nFormat);
+        }
+        else
+        {
+            if (mnPrecision >= mnMaxGeneralPrecision)
+                return;     // early bail out for nothing changes here
 
-            if (nPrec != SvNumberFormatter::UNLIMITED_PRECISION && nPrec > mnPrecision)
-                mnPrecision = nPrec;
+            if (!fVal)
+            {
+                // 0 doesn't change precision, but set a maximum length if none yet.
+                if (!mnMaxLen)
+                    mnMaxLen = 1;
+                return;
+            }
+
+            // Simple number string with at most 15 decimals and trailing
+            // decimal zeros eliminated.
+            aSep = ".";
+            aString = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_F, nCellPrecision, '.', true);
+            nPrec = SvNumberFormatter::UNLIMITED_PRECISION;
         }
 
+        nLen = aString.getLength();
+        if (nLen <= 0)
+            // Ignore empty string.
+            return;
+
+        if (nPrec == SvNumberFormatter::UNLIMITED_PRECISION && mnPrecision < mnMaxGeneralPrecision)
+        {
+            if (nFormat % SV_COUNTRY_LANGUAGE_OFFSET)
+            {
+                // For some reason we couldn't obtain a precision from the
+                // format, retry with simple number string.
+                aSep = ".";
+                aString = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_F, nCellPrecision, '.', true);
+                nLen = aString.getLength();
+            }
+            sal_Int32 nSep = aString.indexOf( aSep);
+            if (nSep != -1)
+                nPrec = aString.getLength() - nSep - 1;
+
+        }
+
+        if (nPrec != SvNumberFormatter::UNLIMITED_PRECISION && nPrec > mnPrecision)
+            mnPrecision = nPrec;
+
         if (mnPrecision)
         {   // less than mnPrecision in string => widen it
             // more => shorten it
-            OUString aSep = mpFormatter->GetFormatDecimalSep(nFormat);
             sal_Int32 nTmp = aString.indexOf(aSep);
             if ( nTmp == -1 )
                 nLen += mnPrecision + aSep.getLength();
@@ -2759,15 +2815,27 @@ class MaxNumStringLenHandler
             }
         }
 
+        // Enlarge for sign if necessary. Bear in mind that
+        // GetMaxNumberStringLen() is for determining dBase decimal field width
+        // and precision where the overall field width must include the sign.
+        // Fitting -1 into "#.##" (width 4, 2 decimals) does not work.
+        if (mbHaveSigned && fVal >= 0.0)
+            ++nLen;
+
         if (mnMaxLen < nLen)
             mnMaxLen = nLen;
     }
 
 public:
-    MaxNumStringLenHandler(const ScColumn& rColumn, sal_uInt16 nPrecision) :
+    MaxNumStringLenHandler(const ScColumn& rColumn, sal_uInt16 nMaxGeneralPrecision) :
         mrColumn(rColumn), mpFormatter(rColumn.GetDoc().GetFormatTable()),
-        mnMaxLen(0), mnPrecision(nPrecision)
+        mnMaxLen(0), mnPrecision(0), mnMaxGeneralPrecision(nMaxGeneralPrecision),
+        mbHaveSigned(false)
     {
+        // Limit the decimals passed to doubleToUString().
+        // Also, the dBaseIII maximum precision is 15.
+        if (mnMaxGeneralPrecision > 15)
+            mnMaxGeneralPrecision = 15;
     }
 
     void operator() (size_t nRow, double fVal)
@@ -2792,12 +2860,8 @@ public:
 xub_StrLen ScColumn::GetMaxNumberStringLen(
     sal_uInt16& nPrecision, SCROW nRowStart, SCROW nRowEnd ) const
 {
-    nPrecision = pDocument->GetDocOptions().GetStdPrecision();
-    if ( nPrecision == SvNumberFormatter::UNLIMITED_PRECISION )
-        // In case of unlimited precision, use 2 instead.
-        nPrecision = 2;
-
-    MaxNumStringLenHandler aFunc(*this, nPrecision);
+    sal_uInt16 nMaxGeneralPrecision = pDocument->GetDocOptions().GetStdPrecision();
+    MaxNumStringLenHandler aFunc(*this, nMaxGeneralPrecision);
     sc::ParseFormulaNumeric(maCells.begin(), maCells, nRowStart, nRowEnd, aFunc);
     nPrecision = aFunc.getPrecision();
     return aFunc.getMaxLen();
diff --git a/sc/source/ui/docshell/docsh8.cxx b/sc/source/ui/docshell/docsh8.cxx
index c0d66c7..22ed874 100644
--- a/sc/source/ui/docshell/docsh8.cxx
+++ b/sc/source/ui/docshell/docsh8.cxx
@@ -517,8 +517,8 @@ void lcl_GetColumnTypes(
         OUString aFieldName;
         OUString aString;
 
-        // Feldname[,Type[,Width[,Prec]]]
-        // Typ etc.: L; D; C[,W]; N[,W[,P]]
+        // Fieldname[,Type[,Width[,Prec]]]
+        // Type etc.: L; D; C[,W]; N[,W[,P]]
         if ( bHasFieldNames )
         {
             aString = pDoc->GetString(nCol, nFirstRow, nTab);
@@ -533,29 +533,30 @@ void lcl_GetColumnTypes(
                     case 'L' :
                         nDbType = sdbc::DataType::BIT;
                         nFieldLen = 1;
-                        bTypeDefined = sal_True;
-                        bPrecDefined = sal_True;
+                        bTypeDefined = true;
+                        bPrecDefined = true;
                         break;
                     case 'D' :
                         nDbType = sdbc::DataType::DATE;
                         nFieldLen = 8;
-                        bTypeDefined = sal_True;
-                        bPrecDefined = sal_True;
+                        bTypeDefined = true;
+                        bPrecDefined = true;
                         break;
                     case 'M' :
                         nDbType = sdbc::DataType::LONGVARCHAR;
                         nFieldLen = 10;
-                        bTypeDefined = sal_True;
-                        bPrecDefined = sal_True;
-                        bHasMemo = sal_True;
+                        bTypeDefined = true;
+                        bPrecDefined = true;
+                        bHasMemo = true;
                         break;
                     case 'C' :
                         nDbType = sdbc::DataType::VARCHAR;
-                        bTypeDefined = sal_True;
-                        bPrecDefined = sal_True;
+                        bTypeDefined = true;
+                        bPrecDefined = true;
                         break;
                     case 'N' :
                         nDbType = sdbc::DataType::DECIMAL;
+                        bTypeDefined = true;
                         break;
                 }
                 if ( bTypeDefined && !nFieldLen && nToken > 2 )
@@ -567,7 +568,9 @@ void lcl_GetColumnTypes(
                         if ( CharClass::isAsciiNumeric(aTmp) )
                         {
                             nPrecision = aTmp.toInt32();
-                            bPrecDefined = sal_True;
+                            if (nPrecision && nFieldLen < nPrecision+1)
+                                nFieldLen = nPrecision + 1;     // include decimal separator
+                            bPrecDefined = true;
                         }
                     }
                 }
@@ -575,11 +578,12 @@ void lcl_GetColumnTypes(
             else
                 aFieldName = aString;
 
-            // Feldnamen pruefen und ggbf. gueltigen Feldnamen erzeugen.
-            // Erstes Zeichen muss Buchstabe sein,
-            // weitere nur alphanumerisch und Unterstrich erlaubt,
-            // "_DBASELOCK" ist reserviert (obsolet weil erstes Zeichen kein Buchstabe),
-            // keine doppelten Namen.
+            // Check field name and generate valid field name if necessary.
+            // First character has to be alphabetical, subsequent characters
+            // have to be alphanumerical or underscore.
+            // "_DBASELOCK" is reserved (obsolete because first character is
+            // not alphabetical).
+            // No duplicated names.
             if ( !IsAsciiAlpha( aFieldName[0] ) )
                 aFieldName = "N" + aFieldName;
             OUString aTmpStr;
@@ -596,7 +600,7 @@ void lcl_GetColumnTypes(
                 aFieldName = aFieldName.copy(0,  10);
 
             if (!aFieldNames.insert(aFieldName).second)
-            {   // doppelter Feldname, numerisch erweitern
+            {   // Duplicated field name, append numeric suffix.
                 sal_uInt16 nSub = 1;
                 OUString aFixPart( aFieldName );
                 do
@@ -616,7 +620,7 @@ void lcl_GetColumnTypes(
         }
 
         if ( !bTypeDefined )
-        {   // Feldtyp
+        {   // Field type.
             ScRefCellValue aCell;
             aCell.assign(*pDoc, ScAddress(nCol, nFirstDataRow, nTab));
             if (aCell.isEmpty() || aCell.hasString())
@@ -646,61 +650,99 @@ void lcl_GetColumnTypes(
         }
         bool bSdbLenAdjusted = false;
         bool bSdbLenBad = false;
-        // Feldlaenge
+        // Field length.
         if ( nDbType == sdbc::DataType::VARCHAR && !nFieldLen )
-        {   // maximale Feldbreite bestimmen
+        {   // Determine maximum field width.
             nFieldLen = pDoc->GetMaxStringLen( nTab, nCol, nFirstDataRow,
                 nLastRow, eCharSet );
             if ( nFieldLen == 0 )
                 nFieldLen = 1;
         }
         else if ( nDbType == sdbc::DataType::DECIMAL )
-        {   // maximale Feldbreite und Nachkommastellen bestimmen
-            xub_StrLen nLen;
+        {   // Determine maximum field width and precision.
+            sal_Int32 nLen;
             sal_uInt16 nPrec;
             nLen = pDoc->GetMaxNumberStringLen( nPrec, nTab, nCol,
                 nFirstDataRow, nLastRow );
-            // dBaseIII Limit Nachkommastellen: 15
+            // dBaseIII precision limit: 15
             if ( nPrecision > 15 )
                 nPrecision = 15;
             if ( nPrec > 15 )
                 nPrec = 15;
             if ( bPrecDefined && nPrecision != nPrec )
-            {   // Laenge auf vorgegebene Nachkommastellen anpassen
-                if ( nPrecision )
-                    nLen = sal::static_int_cast<xub_StrLen>( nLen + ( nPrecision - nPrec ) );
+            {
+                if (nPrecision < nPrec)
+                {
+                    // This is a hairy case. User defined nPrecision but a
+                    // number format has more precision. Modifying a dBase
+                    // field may as well render the resulting file useless for
+                    // an application that relies on its defined structure,
+                    // especially if we are resaving an already existing file.
+                    // So who's right, the user who (or the loaded file that)
+                    // defined the field, or the user who applied the format?
+                    // Commit f59e350d1733125055f1144f8b3b1b0a46f6d1ca gave the
+                    // format a higher priority, which is debatable.
+                    SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field precision for "
+                            << aFieldName << " (" << nPrecision << "<" << nPrec << ")");
+
+                    // Adjust length to larger predefined integer part. There
+                    // may be a reason that the field was prepared for larger
+                    // numbers.
+                    if (nFieldLen - nPrecision > nLen - nPrec)
+                        nLen = nFieldLen - (nPrecision ? nPrecision+1 : 0) + 1 + nPrec;
+                    // And override precision.
+                    nPrecision = nPrec;
+                }
                 else
-                    nLen -= nPrec+1;            // auch den . mit raus
+                {
+                    // Adjust length to predefined precision.
+                    if ( nPrecision )
+                        nLen = nLen + ( nPrecision - nPrec );
+                    else
+                        nLen -= nPrec+1;    // also remove the decimal separator
+                }
+            }
+            if (nFieldLen < nLen)
+            {
+                if (!bTypeDefined)
+                    nFieldLen = nLen;
+                else
+                {
+                    // Again a hairy case and conflict. Furthermore, the
+                    // larger overall length may be a result of only a higher
+                    // precision obtained from formats.
+                    SAL_WARN( "sc", "lcl_GetColumnTypes: conflicting dBase field length for "
+                            << aFieldName << " (" << nFieldLen << "<" << nLen << ")");
+                    nFieldLen = nLen;
+                }
             }
-            if ( nLen > nFieldLen && !bTypeDefined )
-                nFieldLen = nLen;
             if ( !bPrecDefined )
                 nPrecision = nPrec;
             if ( nFieldLen == 0 )
                 nFieldLen = 1;
             else if ( nFieldLen > 19 )
-                nFieldLen = 19;     // dBaseIII Limit Feldlaenge numerisch: 19
+                nFieldLen = 19;     // dBaseIII numeric field length limit: 19
             if ( nPrecision && nFieldLen < nPrecision + 2 )
-                nFieldLen = nPrecision + 2;     // 0. muss mit reinpassen
+                nFieldLen = nPrecision + 2;     // 0. must fit into
             // 538 MUST: Sdb internal representation adds 2 to the field length!
             // To give the user what he wants we must substract it here.
              //! CAVEAT! There is no way to define a numeric field with a length
              //! of 1 and no decimals!
             if ( nFieldLen == 1 && nPrecision == 0 )
-                bSdbLenBad = sal_True;
+                bSdbLenBad = true;
             nFieldLen = SvDbaseConverter::ConvertPrecisionToOdbc( nFieldLen, nPrecision );
-            bSdbLenAdjusted = sal_True;
+            bSdbLenAdjusted = true;
         }
         if ( nFieldLen > 254 )
         {
             if ( nDbType == sdbc::DataType::VARCHAR )
-            {   // zu lang fuer normales Textfeld => Memofeld
+            {   // Too long for a normal text field => memo field.
                 nDbType = sdbc::DataType::LONGVARCHAR;
                 nFieldLen = 10;
-                bHasMemo = sal_True;
+                bHasMemo = true;
             }
             else
-                nFieldLen = 254;                    // dumm gelaufen..
+                nFieldLen = 254;                    // bad luck..
         }
 
         pColNames[nField] = aFieldName;


More information about the Libreoffice-commits mailing list