Back to index

lightning-sunbird  0.9+nobinonly
txXSLTNumberCounters.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is TransforMiiX XSLT processor code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Jonas Sicking.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Jonas Sicking <sicking@bigfoot.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "txXSLTNumber.h"
00040 #include "nsReadableUtils.h"
00041 #include "txCore.h"
00042 
00043 class txDecimalCounter : public txFormattedCounter {
00044 public:
00045     txDecimalCounter() : mMinLength(1), mGroupSize(50)
00046     {
00047     }
00048     
00049     txDecimalCounter(PRInt32 aMinLength, PRInt32 aGroupSize,
00050                      const nsAString& mGroupSeparator);
00051     
00052     virtual void appendNumber(PRInt32 aNumber, nsAString& aDest);
00053 
00054 private:
00055     PRInt32 mMinLength;
00056     PRInt32 mGroupSize;
00057     nsString mGroupSeparator;
00058 };
00059 
00060 class txAlphaCounter : public txFormattedCounter {
00061 public:
00062     txAlphaCounter(PRUnichar aOffset) : mOffset(aOffset)
00063     {
00064     }
00065 
00066     virtual void appendNumber(PRInt32 aNumber, nsAString& aDest);
00067     
00068 private:
00069     PRUnichar mOffset;
00070 };
00071 
00072 class txRomanCounter : public txFormattedCounter {
00073 public:
00074     txRomanCounter(MBool aUpper) : mTableOffset(aUpper ? 30 : 0)
00075     {
00076     }
00077 
00078     void appendNumber(PRInt32 aNumber, nsAString& aDest);
00079 
00080 private:
00081     PRInt32 mTableOffset;
00082 };
00083 
00084 
00085 nsresult
00086 txFormattedCounter::getCounterFor(const nsAFlatString& aToken,
00087                                   PRInt32 aGroupSize,
00088                                   const nsAString& aGroupSeparator,
00089                                   txFormattedCounter*& aCounter)
00090 {
00091     PRInt32 length = aToken.Length();
00092     NS_ASSERTION(length, "getting counter for empty token");
00093     aCounter = 0;
00094     
00095     if (length == 1) {
00096         PRUnichar ch = aToken.CharAt(0);
00097         switch (ch) {
00098 
00099             case 'i':
00100             case 'I':
00101                 aCounter = new txRomanCounter(ch == 'I');
00102                 break;
00103             
00104             case 'a':
00105             case 'A':
00106                 aCounter = new txAlphaCounter(ch);
00107                 break;
00108             
00109             case '1':
00110             default:
00111                 // if we don't recognize the token then use "1"
00112                 aCounter = new txDecimalCounter(1, aGroupSize,
00113                                                 aGroupSeparator);
00114                 break;
00115         }
00116         return aCounter ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00117     }
00118     
00119     // for now, the only multi-char token we support are decimals
00120     PRInt32 i;
00121     for (i = 0; i < length-1; ++i) {
00122         if (aToken.CharAt(i) != '0')
00123             break;
00124     }
00125     if (i == length-1 && aToken.CharAt(i) == '1') {
00126         aCounter = new txDecimalCounter(length, aGroupSize, aGroupSeparator);
00127     }
00128     else {
00129         // if we don't recognize the token then use '1'
00130         aCounter = new txDecimalCounter(1, aGroupSize, aGroupSeparator);
00131     }
00132 
00133     return aCounter ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00134 }
00135 
00136 
00137 txDecimalCounter::txDecimalCounter(PRInt32 aMinLength, PRInt32 aGroupSize,
00138                                    const nsAString& aGroupSeparator)
00139     : mMinLength(aMinLength), mGroupSize(aGroupSize),
00140       mGroupSeparator(aGroupSeparator)
00141 {
00142     if (mGroupSize <= 0) {
00143         mGroupSize = aMinLength + 10;
00144     }
00145 }
00146 
00147 void txDecimalCounter::appendNumber(PRInt32 aNumber, nsAString& aDest)
00148 {
00149     const PRInt32 bufsize = 10; //must be able to fit an PRInt32
00150     PRUnichar buf[bufsize];
00151     PRInt32 pos = bufsize;
00152     while (aNumber > 0) {
00153         PRInt32 ch = aNumber % 10;
00154         aNumber /= 10;
00155         buf[--pos] = ch + '0';
00156     }
00157 
00158     // in case we didn't get a long enough string
00159     PRInt32 end  = (bufsize > mMinLength) ? bufsize - mMinLength : 0;
00160     while (pos > end) {
00161         buf[--pos] = '0';
00162     }
00163     
00164     // in case we *still* didn't get a long enough string.
00165     // this should be very rare since it only happens if mMinLength is bigger
00166     // then the length of any PRInt32.
00167     // pos will always be zero 
00168     PRInt32 extraPos = mMinLength;
00169     while (extraPos > bufsize) {
00170         aDest.Append(PRUnichar('0'));
00171         --extraPos;
00172         if (extraPos % mGroupSize == 0) {
00173             aDest.Append(mGroupSeparator);
00174         }
00175     }
00176 
00177     // copy string to buffer
00178     if (mGroupSize >= bufsize - pos) {
00179         // no grouping will occur
00180         aDest.Append(buf + pos, (PRUint32)(bufsize - pos));
00181     }
00182     else {
00183         // append chars up to first grouping separator
00184         PRInt32 len = ((bufsize - pos - 1) % mGroupSize) + 1;
00185         aDest.Append(buf + pos, len);
00186         pos += len;
00187         while (bufsize - pos > 0) {
00188             aDest.Append(mGroupSeparator);
00189             aDest.Append(buf + pos, mGroupSize);
00190             pos += mGroupSize;
00191         }
00192         NS_ASSERTION(bufsize == pos, "error while grouping");
00193     }
00194 }
00195 
00196 
00197 void txAlphaCounter::appendNumber(PRInt32 aNumber, nsAString& aDest)
00198 {
00199     PRUnichar buf[12];
00200     buf[11] = 0;
00201     PRInt32 pos = 11;
00202     while (aNumber > 0) {
00203         --aNumber;
00204         PRInt32 ch = aNumber % 26;
00205         aNumber /= 26;
00206         buf[--pos] = ch + mOffset;
00207     }
00208     
00209     aDest.Append(buf + pos, (PRUint32)(11 - pos));
00210 }
00211 
00212 
00213 const char* const kTxRomanNumbers[] =
00214     {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm",
00215      "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc",
00216      "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix",
00217      "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM",
00218      "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC",
00219      "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
00220 
00221 void txRomanCounter::appendNumber(PRInt32 aNumber, nsAString& aDest)
00222 {
00223     // Numbers bigger then 3999 can't be done in roman
00224     if (aNumber >= 4000) {
00225         txDecimalCounter().appendNumber(aNumber, aDest);
00226         return;
00227     }
00228 
00229     while (aNumber >= 1000) {
00230         aDest.Append(!mTableOffset ? PRUnichar('m') : PRUnichar('M'));
00231         aNumber -= 1000;
00232     }
00233 
00234     PRInt32 posValue;
00235     
00236     // Hundreds
00237     posValue = aNumber / 100;
00238     aNumber %= 100;
00239     AppendASCIItoUTF16(kTxRomanNumbers[posValue + mTableOffset], aDest);
00240     // Tens
00241     posValue = aNumber / 10;
00242     aNumber %= 10;
00243     AppendASCIItoUTF16(kTxRomanNumbers[10 + posValue + mTableOffset], aDest);
00244     // Ones
00245     AppendASCIItoUTF16(kTxRomanNumbers[20 + aNumber + mTableOffset], aDest);
00246 }