1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #ifndef INCLUDED_UNOTOOLS_DIGITGROUPINGITERATOR_HXX
25 #define INCLUDED_UNOTOOLS_DIGITGROUPINGITERATOR_HXX
26 
27 #include <com/sun/star/uno/Sequence.hxx>
28 
29 namespace utl {
30 
31 /** Iterator to be used with a digit grouping as obtained through
32     LocaleDataWrapper::getDigitGrouping().
33 
34     The iterator advances over the digit groupings, returning the number of
35     digits per group. If the last group was encountered the iterator will
36     always return the last grouping.
37 
38     Grouping values are sanitized to be 0 <= value <= SAL_MAX_UINT16, even if
39     originally Int32, to be able to easily cast it down to String's xub_StrLen.
40     This shouldn't make any difference in practice.
41 
42     Usage example with a string buffer containing a decimal representation of
43     an integer number. Note that of course this loop could be optimized to not
44     count single characters but hunks of groups instead using the get() method,
45     this is just for illustrating usage. Anyway, for double values it is highly
46     more efficient to use ::rtl::math::doubleToString() and pass the grouping
47     sequence, instead of using this iterator and inserting charcters into
48     strings.
49 
50     DigitGroupingIterator aGrouping(...)
51     sal_Int32 nCount = 0;
52     sal_Int32 n = aBuffer.getLength();
53     // >1 because we don't want to insert a separator if there is no leading digit.
54     while (n-- > 1)
55     {
56         if (++nCount >= aGrouping.getPos())
57         {
58             aBuffer.insert( n, cSeparator);
59             nGroupDigits = aGrouping.advance();
60         }
61     }
62 
63  */
64 
65 class DigitGroupingIterator
66 {
67     const ::com::sun::star::uno::Sequence< sal_Int32 > maGroupings;
68 
69     sal_Int32   mnGroup;        // current active grouping
70     sal_Int32   mnDigits;       // current active digits per group
71     sal_Int32   mnNextPos;      // position (in digits) of next grouping
72 
setInfinite()73     void setInfinite()
74     {
75         mnGroup = maGroupings.getLength();
76     }
77 
isInfinite() const78     bool isInfinite() const
79     {
80         return mnGroup >= maGroupings.getLength();
81     }
82 
getGrouping() const83     sal_Int32 getGrouping() const
84     {
85         if (mnGroup < maGroupings.getLength())
86         {
87             sal_Int32 n = maGroupings[mnGroup];
88             OSL_ENSURE( 0 <= n && n <= SAL_MAX_UINT16, "DigitGroupingIterator::getGrouping: far out");
89             if (n < 0)
90                 n = 0;                  // sanitize ...
91             else if (n > SAL_MAX_UINT16)
92                 n = SAL_MAX_UINT16;     // limit for use with xub_StrLen
93             return n;
94         }
95         return 0;
96     }
97 
setPos()98     void setPos()
99     {
100         // someone might be playing jokes on us, so check for overflow
101         if (mnNextPos <= SAL_MAX_INT32 - mnDigits)
102             mnNextPos += mnDigits;
103     }
104 
setDigits()105     void setDigits()
106     {
107         sal_Int32 nPrev = mnDigits;
108         mnDigits = getGrouping();
109         if (!mnDigits)
110         {
111             mnDigits = nPrev;
112             setInfinite();
113         }
114         setPos();
115     }
116 
initGrouping()117     void initGrouping()
118     {
119         mnDigits = 3;       // just in case of constructed with empty grouping
120         mnGroup = 0;
121         mnNextPos = 0;
122         setDigits();
123     }
124 
125     // not implemented, prevent usage
126     DigitGroupingIterator();
127     DigitGroupingIterator( const DigitGroupingIterator & );
128     DigitGroupingIterator & operator=( const DigitGroupingIterator & );
129 
130 public:
131 
DigitGroupingIterator(const::com::sun::star::uno::Sequence<sal_Int32> & rGroupings)132     explicit DigitGroupingIterator( const ::com::sun::star::uno::Sequence< sal_Int32 > & rGroupings )
133         : maGroupings( rGroupings)
134     {
135         initGrouping();
136     }
137 
138     /** Advance iterator to next grouping. */
advance()139     DigitGroupingIterator & advance()
140     {
141         if (isInfinite())
142             setPos();
143         else
144         {
145             ++mnGroup;
146             setDigits();
147         }
148         return *this;
149     }
150 
151     /** Obtain current grouping. Always > 0. */
get() const152     sal_Int32 get() const
153     {
154         return mnDigits;
155     }
156 
157     /** The next position (in integer digits) from the right where to insert a
158         group separator. */
getPos()159     sal_Int32 getPos()
160     {
161         return mnNextPos;
162     }
163 
164     /** Reset iterator to start again from the right beginning. */
reset()165     void reset()
166     {
167         initGrouping();
168     }
169 
170     /** Create a sequence of bool values containing positions where to add a
171         separator when iterating forward over a string and copying digit per
172         digit. For example, for grouping in thousands and nIntegerDigits==7 the
173         sequence returned would be {1,0,0,1,0,0,0} so the caller would add a
174         separator after the 1st and the 4th digit. */
createForwardSequence(sal_Int32 nIntegerDigits,const::com::sun::star::uno::Sequence<sal_Int32> & rGroupings)175     static ::com::sun::star::uno::Sequence< sal_Bool > createForwardSequence(
176             sal_Int32 nIntegerDigits,
177             const ::com::sun::star::uno::Sequence< sal_Int32 > & rGroupings )
178     {
179         if (nIntegerDigits <= 0)
180             return ::com::sun::star::uno::Sequence< sal_Bool >();
181         DigitGroupingIterator aIterator( rGroupings);
182         ::com::sun::star::uno::Sequence< sal_Bool > aSeq( nIntegerDigits);
183         sal_Bool* pArr = aSeq.getArray();
184         for (sal_Int32 j = 0; --nIntegerDigits >= 0; ++j)
185         {
186             if (j == aIterator.getPos())
187             {
188                 pArr[nIntegerDigits] = sal_True;
189                 aIterator.advance();
190             }
191             else
192                 pArr[nIntegerDigits] = sal_False;
193         }
194         return aSeq;
195     }
196 };
197 
198 } // namespace utl
199 
200 #endif // INCLUDED_UNOTOOLS_DIGITGROUPINGITERATOR_HXX
201