| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 1999 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 1999, Lotus |
| * Development Corporation., http://www.lotus.com. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| package org.apache.xalan.transformer; |
| |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| |
| import java.util.Locale; |
| import java.util.NoSuchElementException; |
| |
| /** |
| * <meta name="usage" content="internal"/> |
| * Converts enumerated numbers into strings, using the XSL conversion attributes. |
| * Having this in a class helps avoid being forced to extract the attributes repeatedly. |
| */ |
| class NumeratorFormatter |
| { |
| |
| /** The owning xsl:number element. */ |
| protected Element m_xslNumberElement; |
| |
| /** An instance of a Tokenizer */ |
| NumberFormatStringTokenizer m_formatTokenizer; |
| |
| /** Locale we need to format in */ |
| Locale m_locale; |
| |
| /** An instance of a NumberFormat */ |
| java.text.NumberFormat m_formatter; |
| |
| /** An instance of a transformer */ |
| TransformerImpl m_processor; |
| |
| /** |
| * Table to help in converting decimals to roman numerals. |
| * @see org.apache.xalan.transformer.DecimalToRoman |
| */ |
| private final static DecimalToRoman m_romanConvertTable[] = { |
| new DecimalToRoman(1000, "M", 900, "CM"), |
| new DecimalToRoman(500, "D", 400, "CD"), |
| new DecimalToRoman(100L, "C", 90L, "XC"), |
| new DecimalToRoman(50L, "L", 40L, "XL"), |
| new DecimalToRoman(10L, "X", 9L, "IX"), |
| new DecimalToRoman(5L, "V", 4L, "IV"), |
| new DecimalToRoman(1L, "I", 1L, "I") }; |
| |
| /** |
| * Chars for converting integers into alpha counts. |
| * @see TransformerImpl#int2alphaCount |
| */ |
| private final static char[] m_alphaCountTable = { 'Z', // z for zero |
| 'A', 'B', 'C', 'D', 'E', |
| 'F', 'G', 'H', 'I', 'J', |
| 'K', 'L', 'M', 'N', 'O', |
| 'P', 'Q', 'R', 'S', 'T', |
| 'U', 'V', 'W', 'X', 'Y' }; |
| |
| /** |
| * Construct a NumeratorFormatter using an element |
| * that contains XSL number conversion attributes - |
| * format, letter-value, xml:lang, digit-group-sep, |
| * n-digits-per-group, and sequence-src. |
| * |
| * @param xslNumberElement The given xsl:number element |
| * @param processor a non-null transformer instance |
| */ |
| NumeratorFormatter(Element xslNumberElement, TransformerImpl processor) |
| { |
| m_xslNumberElement = xslNumberElement; |
| m_processor = processor; |
| } // end NumeratorFormatter(Element) constructor |
| |
| /** |
| * Convert a long integer into alphabetic counting, in other words |
| * count using the sequence A B C ... Z AA AB AC.... etc. |
| * |
| * @param val Value to convert -- must be greater than zero. |
| * @param table a table containing one character for each digit in the radix |
| * @return String representing alpha count of number. |
| * @see org.apache.xalan.transformer.DecimalToRoman |
| * |
| * Note that the radix of the conversion is inferred from the size |
| * of the table. |
| */ |
| protected String int2alphaCount(int val, char[] table) |
| { |
| |
| int radix = table.length; |
| |
| // Create a buffer to hold the result |
| // TODO: size of the table can be detereined by computing |
| // logs of the radix. For now, we fake it. |
| char buf[] = new char[100]; |
| |
| // next character to set in the buffer |
| int charPos = buf.length - 1; // work backward through buf[] |
| |
| // index in table of the last character that we stored |
| int lookupIndex = 1; // start off with anything other than zero to make correction work |
| |
| // Correction number |
| // |
| // Correction can take on exactly two values: |
| // |
| // 0 if the next character is to be emitted is usual |
| // |
| // radix - 1 |
| // if the next char to be emitted should be one less than |
| // you would expect |
| // |
| // For example, consider radix 10, where 1="A" and 10="J" |
| // |
| // In this scheme, we count: A, B, C ... H, I, J (not A0 and certainly |
| // not AJ), A1 |
| // |
| // So, how do we keep from emitting AJ for 10? After correctly emitting the |
| // J, lookupIndex is zero. We now compute a correction number of 9 (radix-1). |
| // In the following line, we'll compute (val+correction) % radix, which is, |
| // (val+9)/10. By this time, val is 1, so we compute (1+9) % 10, which |
| // is 10 % 10 or zero. So, we'll prepare to emit "JJ", but then we'll |
| // later suppress the leading J as representing zero (in the mod system, |
| // it can represent either 10 or zero). In summary, the correction value of |
| // "radix-1" acts like "-1" when run through the mod operator, but with the |
| // desireable characteristic that it never produces a negative number. |
| int correction = 0; |
| |
| // TODO: throw error on out of range input |
| do |
| { |
| |
| // most of the correction calculation is explained above, the reason for the |
| // term after the "|| " is that it correctly propagates carries across |
| // multiple columns. |
| correction = |
| ((lookupIndex == 0) || (correction != 0 && lookupIndex == radix - 1)) |
| ? (radix - 1) : 0; |
| |
| // index in "table" of the next char to emit |
| lookupIndex = (val + correction) % radix; |
| |
| // shift input by one "column" |
| val = (val / radix); |
| |
| // if the next value we'd put out would be a leading zero, we're done. |
| if (lookupIndex == 0 && val == 0) |
| break; |
| |
| // put out the next character of output |
| buf[charPos--] = table[lookupIndex]; |
| } |
| while (val > 0); |
| |
| return new String(buf, charPos + 1, (buf.length - charPos - 1)); |
| } |
| |
| /** |
| * Convert a long integer into roman numerals. |
| * @param val Value to convert. |
| * @param prefixesAreOK true_ to enable prefix notation (e.g. 4 = "IV"), |
| * false_ to disable prefix notation (e.g. 4 = "IIII"). |
| * @return Roman numeral string. |
| * @see DecimalToRoman |
| * @see m_romanConvertTable |
| */ |
| String long2roman(long val, boolean prefixesAreOK) |
| { |
| |
| if (val <= 0) |
| { |
| return "#E(" + val + ")"; |
| } |
| |
| String roman = ""; |
| int place = 0; |
| |
| if (val <= 3999L) |
| { |
| do |
| { |
| while (val >= m_romanConvertTable[place].m_postValue) |
| { |
| roman += m_romanConvertTable[place].m_postLetter; |
| val -= m_romanConvertTable[place].m_postValue; |
| } |
| |
| if (prefixesAreOK) |
| { |
| if (val >= m_romanConvertTable[place].m_preValue) |
| { |
| roman += m_romanConvertTable[place].m_preLetter; |
| val -= m_romanConvertTable[place].m_preValue; |
| } |
| } |
| |
| place++; |
| } |
| while (val > 0); |
| } |
| else |
| { |
| roman = "#error"; |
| } |
| |
| return roman; |
| } // end long2roman |
| |
| /** |
| * This class returns tokens using non-alphanumberic |
| * characters as delimiters. |
| */ |
| class NumberFormatStringTokenizer |
| { |
| |
| /** Field holding the current position in the string */ |
| private int currentPosition; |
| |
| /** The total length of the string */ |
| private int maxPosition; |
| |
| /** The string to tokenize */ |
| private String str; |
| |
| /** |
| * Construct a NumberFormatStringTokenizer. |
| * |
| * @param str The string to tokenize |
| */ |
| NumberFormatStringTokenizer(String str) |
| { |
| this.str = str; |
| maxPosition = str.length(); |
| } |
| |
| /** |
| * Reset tokenizer so that nextToken() starts from the beginning. |
| * |
| */ |
| void reset() |
| { |
| currentPosition = 0; |
| } |
| |
| /** |
| * Returns the next token from this string tokenizer. |
| * |
| * @return the next token from this string tokenizer. |
| * @throws NoSuchElementException if there are no more tokens in this |
| * tokenizer's string. |
| */ |
| String nextToken() |
| { |
| |
| if (currentPosition >= maxPosition) |
| { |
| throw new NoSuchElementException(); |
| } |
| |
| int start = currentPosition; |
| |
| while ((currentPosition < maxPosition) |
| && Character.isLetterOrDigit(str.charAt(currentPosition))) |
| { |
| currentPosition++; |
| } |
| |
| if ((start == currentPosition) |
| && (!Character.isLetterOrDigit(str.charAt(currentPosition)))) |
| { |
| currentPosition++; |
| } |
| |
| return str.substring(start, currentPosition); |
| } |
| |
| /** |
| * Tells if <code>nextToken</code> will throw an exception * if it is called. |
| * |
| * @return true if <code>nextToken</code> can be called * without throwing an exception. |
| */ |
| boolean hasMoreTokens() |
| { |
| return (currentPosition >= maxPosition) ? false : true; |
| } |
| |
| /** |
| * Calculates the number of times that this tokenizer's |
| * <code>nextToken</code> method can be called before it generates an |
| * exception. |
| * |
| * @return the number of tokens remaining in the string using the current |
| * delimiter set. |
| * @see java.util.StringTokenizer#nextToken() |
| */ |
| int countTokens() |
| { |
| |
| int count = 0; |
| int currpos = currentPosition; |
| |
| while (currpos < maxPosition) |
| { |
| int start = currpos; |
| |
| while ((currpos < maxPosition) |
| && Character.isLetterOrDigit(str.charAt(currpos))) |
| { |
| currpos++; |
| } |
| |
| if ((start == currpos) |
| && (Character.isLetterOrDigit(str.charAt(currpos)) == false)) |
| { |
| currpos++; |
| } |
| |
| count++; |
| } |
| |
| return count; |
| } |
| } // end NumberFormatStringTokenizer |
| } |