xref: /AOO41X/main/unotools/inc/unotools/digitgroupingiterator.hxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #ifndef INCLUDED_UNOTOOLS_DIGITGROUPINGITERATOR_HXX
29 #define INCLUDED_UNOTOOLS_DIGITGROUPINGITERATOR_HXX
30 
31 #include <com/sun/star/uno/Sequence.hxx>
32 
33 namespace utl {
34 
35 /** Iterator to be used with a digit grouping as obtained through
36     LocaleDataWrapper::getDigitGrouping().
37 
38     The iterator advances over the digit groupings, returning the number of
39     digits per group. If the last group was encountered the iterator will
40     always return the last grouping.
41 
42     Grouping values are sanitized to be 0 <= value <= SAL_MAX_UINT16, even if
43     originally Int32, to be able to easily cast it down to String's xub_StrLen.
44     This shouldn't make any difference in practice.
45 
46     Usage example with a string buffer containing a decimal representation of
47     an integer number. Note that of course this loop could be optimized to not
48     count single characters but hunks of groups instead using the get() method,
49     this is just for illustrating usage. Anyway, for double values it is highly
50     more efficient to use ::rtl::math::doubleToString() and pass the grouping
51     sequence, instead of using this iterator and inserting charcters into
52     strings.
53 
54     DigitGroupingIterator aGrouping(...)
55     sal_Int32 nCount = 0;
56     sal_Int32 n = aBuffer.getLength();
57     // >1 because we don't want to insert a separator if there is no leading digit.
58     while (n-- > 1)
59     {
60         if (++nCount >= aGrouping.getPos())
61         {
62             aBuffer.insert( n, cSeparator);
63             nGroupDigits = aGrouping.advance();
64         }
65     }
66 
67  */
68 
69 class DigitGroupingIterator
70 {
71     const ::com::sun::star::uno::Sequence< sal_Int32 > maGroupings;
72 
73     sal_Int32   mnGroup;        // current active grouping
74     sal_Int32   mnDigits;       // current active digits per group
75     sal_Int32   mnNextPos;      // position (in digits) of next grouping
76 
77     void setInfinite()
78     {
79         mnGroup = maGroupings.getLength();
80     }
81 
82     bool isInfinite() const
83     {
84         return mnGroup >= maGroupings.getLength();
85     }
86 
87     sal_Int32 getGrouping() const
88     {
89         if (mnGroup < maGroupings.getLength())
90         {
91             sal_Int32 n = maGroupings[mnGroup];
92             OSL_ENSURE( 0 <= n && n <= SAL_MAX_UINT16, "DigitGroupingIterator::getGrouping: far out");
93             if (n < 0)
94                 n = 0;                  // sanitize ...
95             else if (n > SAL_MAX_UINT16)
96                 n = SAL_MAX_UINT16;     // limit for use with xub_StrLen
97             return n;
98         }
99         return 0;
100     }
101 
102     void setPos()
103     {
104         // someone might be playing jokes on us, so check for overflow
105         if (mnNextPos <= SAL_MAX_INT32 - mnDigits)
106             mnNextPos += mnDigits;
107     }
108 
109     void setDigits()
110     {
111         sal_Int32 nPrev = mnDigits;
112         mnDigits = getGrouping();
113         if (!mnDigits)
114         {
115             mnDigits = nPrev;
116             setInfinite();
117         }
118         setPos();
119     }
120 
121     void initGrouping()
122     {
123         mnDigits = 3;       // just in case of constructed with empty grouping
124         mnGroup = 0;
125         mnNextPos = 0;
126         setDigits();
127     }
128 
129     // not implemented, prevent usage
130     DigitGroupingIterator();
131     DigitGroupingIterator( const DigitGroupingIterator & );
132     DigitGroupingIterator & operator=( const DigitGroupingIterator & );
133 
134 public:
135 
136     explicit DigitGroupingIterator( const ::com::sun::star::uno::Sequence< sal_Int32 > & rGroupings )
137         : maGroupings( rGroupings)
138     {
139         initGrouping();
140     }
141 
142     /** Advance iterator to next grouping. */
143     DigitGroupingIterator & advance()
144     {
145         if (isInfinite())
146             setPos();
147         else
148         {
149             ++mnGroup;
150             setDigits();
151         }
152         return *this;
153     }
154 
155     /** Obtain current grouping. Always > 0. */
156     sal_Int32 get() const
157     {
158         return mnDigits;
159     }
160 
161     /** The next position (in integer digits) from the right where to insert a
162         group separator. */
163     sal_Int32 getPos()
164     {
165         return mnNextPos;
166     }
167 
168     /** Reset iterator to start again from the right beginning. */
169     void reset()
170     {
171         initGrouping();
172     }
173 
174     /** Create a sequence of bool values containing positions where to add a
175         separator when iterating forward over a string and copying digit per
176         digit. For example, for grouping in thousands and nIntegerDigits==7 the
177         sequence returned would be {1,0,0,1,0,0,0} so the caller would add a
178         separator after the 1st and the 4th digit. */
179     static ::com::sun::star::uno::Sequence< sal_Bool > createForwardSequence(
180             sal_Int32 nIntegerDigits,
181             const ::com::sun::star::uno::Sequence< sal_Int32 > & rGroupings )
182     {
183         if (nIntegerDigits <= 0)
184             return ::com::sun::star::uno::Sequence< sal_Bool >();
185         DigitGroupingIterator aIterator( rGroupings);
186         ::com::sun::star::uno::Sequence< sal_Bool > aSeq( nIntegerDigits);
187         sal_Bool* pArr = aSeq.getArray();
188         for (sal_Int32 j = 0; --nIntegerDigits >= 0; ++j)
189         {
190             if (j == aIterator.getPos())
191             {
192                 pArr[nIntegerDigits] = sal_True;
193                 aIterator.advance();
194             }
195             else
196                 pArr[nIntegerDigits] = sal_False;
197         }
198         return aSeq;
199     }
200 };
201 
202 } // namespace utl
203 
204 #endif // INCLUDED_UNOTOOLS_DIGITGROUPINGITERATOR_HXX
205