summaryrefslogtreecommitdiffstats
path: root/include/unotools/digitgroupingiterator.hxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--include/unotools/digitgroupingiterator.hxx194
1 files changed, 194 insertions, 0 deletions
diff --git a/include/unotools/digitgroupingiterator.hxx b/include/unotools/digitgroupingiterator.hxx
new file mode 100644
index 000000000..7206f72f1
--- /dev/null
+++ b/include/unotools/digitgroupingiterator.hxx
@@ -0,0 +1,194 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_UNOTOOLS_DIGITGROUPINGITERATOR_HXX
+#define INCLUDED_UNOTOOLS_DIGITGROUPINGITERATOR_HXX
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <sal/log.hxx>
+
+namespace utl {
+
+/** Iterator to be used with a digit grouping as obtained through
+ LocaleDataWrapper::getDigitGrouping().
+
+ The iterator advances over the digit groupings, returning the number of
+ digits per group. If the last group was encountered the iterator will
+ always return the last grouping.
+
+ Grouping values are sanitized to be >= 0, even if originally signed
+ sal_Int32.
+
+ Usage example with a string buffer containing a decimal representation of
+ an integer number. Note that of course this loop could be optimized to not
+ count single characters but hunks of groups instead using the get() method,
+ this is just for illustrating usage. Anyway, for double values it is highly
+ more efficient to use ::rtl::math::doubleToString() and pass the grouping
+ sequence, instead of using this iterator and inserting characters into
+ strings.
+
+ DigitGroupingIterator aGrouping(...)
+ sal_Int32 nCount = 0;
+ sal_Int32 n = aBuffer.getLength();
+ // >1 because we don't want to insert a separator if there is no leading digit.
+ while (n-- > 1)
+ {
+ if (++nCount >= aGrouping.getPos())
+ {
+ aBuffer.insert( n, cSeparator);
+ nGroupDigits = aGrouping.advance();
+ }
+ }
+
+ */
+
+class DigitGroupingIterator
+{
+ const css::uno::Sequence< sal_Int32 > maGroupings;
+
+ sal_Int32 mnGroup; // current active grouping
+ sal_Int32 mnDigits; // current active digits per group
+ sal_Int32 mnNextPos; // position (in digits) of next grouping
+
+ void setInfinite()
+ {
+ mnGroup = maGroupings.getLength();
+ }
+
+ bool isInfinite() const
+ {
+ return mnGroup >= maGroupings.getLength();
+ }
+
+ sal_Int32 getGrouping() const
+ {
+ if (mnGroup < maGroupings.getLength())
+ {
+ sal_Int32 n = maGroupings[mnGroup];
+ SAL_WARN_IF( n < 0, "unotools.i18n", "DigitGroupingIterator::getGrouping: negative grouping");
+ if (n < 0)
+ n = 0; // sanitize ...
+ return n;
+ }
+ return 0;
+ }
+
+ void setPos()
+ {
+ // someone might be playing jokes on us, so check for overflow
+ if (mnNextPos <= SAL_MAX_INT32 - mnDigits)
+ mnNextPos += mnDigits;
+ }
+
+ void setDigits()
+ {
+ sal_Int32 nPrev = mnDigits;
+ mnDigits = getGrouping();
+ if (!mnDigits)
+ {
+ mnDigits = nPrev;
+ setInfinite();
+ }
+ setPos();
+ }
+
+ void initGrouping()
+ {
+ mnDigits = 3; // just in case of constructed with empty grouping
+ mnGroup = 0;
+ mnNextPos = 0;
+ setDigits();
+ }
+
+ DigitGroupingIterator( const DigitGroupingIterator & ) = delete;
+ DigitGroupingIterator & operator=( const DigitGroupingIterator & ) = delete;
+
+public:
+
+ explicit DigitGroupingIterator( const css::uno::Sequence< sal_Int32 > & rGroupings )
+ : maGroupings( rGroupings)
+ {
+ initGrouping();
+ }
+
+ /** Advance iterator to next grouping. */
+ DigitGroupingIterator & advance()
+ {
+ if (isInfinite())
+ setPos();
+ else
+ {
+ ++mnGroup;
+ setDigits();
+ }
+ return *this;
+ }
+
+ /** Obtain current grouping. Always > 0. */
+ sal_Int32 get() const
+ {
+ return mnDigits;
+ }
+
+ /** The next position (in integer digits) from the right where to insert a
+ group separator. */
+ sal_Int32 getPos() const
+ {
+ return mnNextPos;
+ }
+
+ /** Reset iterator to start again from the right beginning. */
+ void reset()
+ {
+ initGrouping();
+ }
+
+ /** Create a sequence of bool values containing positions where to add a
+ separator when iterating forward over a string and copying digit per
+ digit. For example, for grouping in thousands and nIntegerDigits==7 the
+ sequence returned would be {1,0,0,1,0,0,0} so the caller would add a
+ separator after the 1st and the 4th digit. */
+ static css::uno::Sequence< sal_Bool > createForwardSequence(
+ sal_Int32 nIntegerDigits,
+ const css::uno::Sequence< sal_Int32 > & rGroupings )
+ {
+ if (nIntegerDigits <= 0)
+ return css::uno::Sequence< sal_Bool >();
+ DigitGroupingIterator aIterator( rGroupings);
+ css::uno::Sequence< sal_Bool > aSeq( nIntegerDigits);
+ sal_Bool* pArr = aSeq.getArray();
+ for (sal_Int32 j = 0; --nIntegerDigits >= 0; ++j)
+ {
+ if (j == aIterator.getPos())
+ {
+ pArr[nIntegerDigits] = true;
+ aIterator.advance();
+ }
+ else
+ pArr[nIntegerDigits] = false;
+ }
+ return aSeq;
+ }
+};
+
+} // namespace utl
+
+#endif // INCLUDED_UNOTOOLS_DIGITGROUPINGITERATOR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */