xmlgraphics-fop-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From spepp...@apache.org
Subject svn commit: r1185530 [3/14] - in /xmlgraphics/fop/branches/Temp_ComplexScripts: ./ src/documentation/content/xdocs/trunk/ src/foschema/ src/java/org/apache/fop/apps/ src/java/org/apache/fop/area/ src/java/org/apache/fop/cli/ src/java/org/apache/fop/fo/...
Date Tue, 18 Oct 2011 08:33:40 GMT
Added: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/util/NumberConverter.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/util/NumberConverter.java?rev=1185530&view=auto
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/util/NumberConverter.java (added)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/util/NumberConverter.java Tue Oct 18 08:33:32 2011
@@ -0,0 +1,1616 @@
+/*
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+// CSOFF: LineLengthCheck
+// CSOFF: InnerAssignmentCheck
+// CSOFF: NoWhitespaceAfterCheck
+// CSOFF: AvoidNestedBlocksCheck
+
+/**
+ * Implementation of Number to String Conversion algorithm specified by
+ * XSL Transformations (XSLT) Version 2.0, W3C Recommendation, 23 January 2007.
+ *
+ * This algorithm differs from that specified in XSLT 1.0 in the following
+ * ways:
+ * <ul>
+ * <li>input numbers are greater than or equal to zero rather than greater than zero;</li>
+ * <li>introduces format tokens { w, W, Ww };</li>
+ * <li>introduces ordinal parameter to generate ordinal numbers;</li>
+ * </ul>
+ *
+ * Implementation Defaults and Limitations
+ * <ul>
+ * <li>If language parameter is unspecified (null or empty string), then the value
+ * of DEFAULT_LANGUAGE is used, which is defined below as "eng" (English).</li>
+ * <li>Only English, French, and Spanish word numerals are supported, and only if less than one trillion (1,000,000,000,000).</li>
+ * <li>Ordinal word numerals are supported for French and Spanish only when less than or equal to ten (10).</li>
+ * </ul>
+ *
+ * Implementation Notes
+ * <ul>
+ * <li>In order to handle format tokens outside the Unicode BMP, all processing is
+ * done in Unicode Scalar Values represented with Integer and Integer[]
+ * types. Without affecting behavior, this may be subsequently optimized to
+ * use int and int[] types.</li>
+ * <li>In order to communicate various sub-parameters, including ordinalization, a <em>features</em>
+ * is employed, which consists of comma separated name and optional value tokens, where name and value
+ * are separated by an equals '=' sign.</li>
+ * <li>Ordinal numbers are selected by specifying a word based format token in combination with a  'ordinal' feature with no value, in which case
+ * the features 'male' and 'female' may be used to specify gender for gender sensitive languages. For example, the feature string "ordinal,female"
+ * selects female ordinals.</li>
+ * </ul>
+ *
+ * @author Glenn Adams
+ */
+public class NumberConverter {
+
+    /** alphabetical  */
+    public static final int LETTER_VALUE_ALPHABETIC = 1;
+    /** traditional  */
+    public static final int LETTER_VALUE_TRADITIONAL = 2;
+
+    /** no token type */
+    private static final int TOKEN_NONE = 0;
+    /** alhphanumeric token type */
+    private static final int TOKEN_ALPHANUMERIC = 1;
+    /** nonalphanumeric token type */
+    private static final int TOKEN_NONALPHANUMERIC = 2;
+    /** default token */
+    private static final Integer[] DEFAULT_TOKEN = new Integer[] { (int) '1' };
+    /** default separator */
+    private static final Integer[] DEFAULT_SEPARATOR = new Integer[] { (int) '.' };
+    /** default language */
+    private static final String DEFAULT_LANGUAGE = "eng";
+
+    /** prefix token */
+    private Integer[] prefix;
+    /** suffix token */
+    private Integer[] suffix;
+    /** sequence of tokens, as parsed from format */
+    private Integer[][] tokens;
+    /** sequence of separators, as parsed from format */
+    private Integer[][] separators;
+    /** grouping separator */
+    private int groupingSeparator;
+    /** grouping size */
+    private int groupingSize;
+    /** letter value */
+    private int letterValue;
+    /** letter value system */
+    private String features;
+    /** language */
+    private String language;
+    /** country */
+    private String country;
+
+    /**
+     * Construct parameterized number converter.
+     * @param format format for the page number (may be null or empty, which is treated as null)
+     * @param groupingSeparator grouping separator (if zero, then no grouping separator applies)
+     * @param groupingSize grouping size (if zero or negative, then no grouping size applies)
+     * @param letterValue letter value (must be one of the above letter value enumeration values)
+     * @param features features (feature sub-parameters)
+     * @param language (may be null or empty, which is treated as null)
+     * @param country (may be null or empty, which is treated as null)
+     * @throws IllegalArgumentException if format is not a valid UTF-16 string (e.g., has unpaired surrogate)
+     */
+    public NumberConverter ( String format, int groupingSeparator, int groupingSize, int letterValue, String features, String language, String country )
+        throws IllegalArgumentException {
+        this.groupingSeparator = groupingSeparator;
+        this.groupingSize = groupingSize;
+        this.letterValue = letterValue;
+        this.features = features;
+        this.language = ( language != null ) ? language.toLowerCase() : null;
+        this.country = ( country != null ) ? country.toLowerCase() : null;
+        parseFormatTokens ( format );
+    }
+
+    /**
+     * Convert a number to string according to conversion parameters.
+     * @param number number to conver
+     * @return string representing converted number
+     */
+    public String convert ( long number ) {
+        List<Long> numbers = new ArrayList<Long>();
+        numbers.add ( number );
+        return convert ( numbers );
+    }
+
+    /**
+     * Convert list of numbers to string according to conversion parameters.
+     * @param numbers list of numbers to convert
+     * @return string representing converted list of numbers
+     */
+    public String convert ( List<Long> numbers ) {
+        List<Integer> scalars = new ArrayList<Integer>();
+        if ( prefix != null ) {
+            appendScalars ( scalars, prefix );
+        }
+        convertNumbers ( scalars, numbers );
+        if ( suffix != null ) {
+            appendScalars ( scalars, suffix );
+        }
+        return scalarsToString ( scalars );
+    }
+
+    private void parseFormatTokens ( String format ) throws IllegalArgumentException {
+        List<Integer[]> tokens = new ArrayList<Integer[]>();
+        List<Integer[]> separators = new ArrayList<Integer[]>();
+        if ( ( format == null ) || ( format.length() == 0 ) ) {
+            format = "1";
+        }
+        int tokenType = TOKEN_NONE;
+        List<Integer> token = new ArrayList<Integer>();
+        Integer[] ca = CharUtilities.toUTF32 ( format, 0, true );
+        for ( int i = 0, n = ca.length; i < n; i++ ) {
+            int c = ca[i];
+            int tokenTypeNew = isAlphaNumeric ( c ) ? TOKEN_ALPHANUMERIC : TOKEN_NONALPHANUMERIC;
+            if ( tokenTypeNew != tokenType ) {
+                if ( token.size() > 0 ) {
+                    if ( tokenType == TOKEN_ALPHANUMERIC ) {
+                        tokens.add ( token.toArray ( new Integer [ token.size() ]  ) );
+                    } else {
+                        separators.add ( token.toArray ( new Integer [ token.size() ]  ) );
+                    }
+                    token.clear();
+                }
+                tokenType = tokenTypeNew;
+            }
+            token.add ( c );
+        }
+        if ( token.size() > 0 ) {
+            if ( tokenType == TOKEN_ALPHANUMERIC ) {
+                tokens.add ( token.toArray ( new Integer [ token.size() ]  ) );
+            } else {
+                separators.add ( token.toArray ( new Integer [ token.size() ]  ) );
+            }
+        }
+        if ( ! separators.isEmpty() ) {
+            this.prefix = separators.remove ( 0 );
+        }
+        if ( ! separators.isEmpty() ) {
+            this.suffix = separators.remove ( separators.size() - 1 );
+        }
+        this.separators = separators.toArray ( new Integer [ separators.size() ] [] );
+        this.tokens = tokens.toArray ( new Integer [ tokens.size() ] [] );
+    }
+
+    private static boolean isAlphaNumeric ( int c ) {
+        switch ( Character.getType ( c ) ) {
+        case Character.DECIMAL_DIGIT_NUMBER:    // Nd
+        case Character.LETTER_NUMBER:           // Nl
+        case Character.OTHER_NUMBER:            // No
+        case Character.UPPERCASE_LETTER:        // Lu
+        case Character.LOWERCASE_LETTER:        // Ll
+        case Character.TITLECASE_LETTER:        // Lt
+        case Character.MODIFIER_LETTER:         // Lm
+        case Character.OTHER_LETTER:            // Lo
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    private void convertNumbers ( List<Integer> scalars, List<Long> numbers ) {
+        Integer[] tknLast = DEFAULT_TOKEN;
+        int   tknIndex = 0;
+        int   tknCount = tokens.length;
+        int   sepIndex = 0;
+        int   sepCount = separators.length;
+        int   numIndex = 0;
+        for ( Long number : numbers ) {
+            Integer[] sep = null;
+            Integer[] tkn;
+            if ( tknIndex < tknCount ) {
+                if ( numIndex > 0 ) {
+                    if ( sepIndex < sepCount ) {
+                        sep = separators [ sepIndex++ ];
+                    } else {
+                        sep = DEFAULT_SEPARATOR;
+                    }
+                }
+                tkn = tokens [ tknIndex++ ];
+            } else {
+                tkn = tknLast;
+            }
+            appendScalars ( scalars, convertNumber ( number, sep, tkn ) );
+            tknLast = tkn;
+            numIndex++;
+        }
+    }
+
+    private Integer[] convertNumber ( long number, Integer[] separator, Integer[] token ) {
+        List<Integer> sl = new ArrayList<Integer>();
+        if ( separator != null ) {
+            appendScalars ( sl, separator );
+        }
+        if ( token != null ) {
+            appendScalars ( sl, formatNumber ( number, token ) );
+        }
+        return sl.toArray ( new Integer [ sl.size() ] );
+    }
+
+    private Integer[] formatNumber ( long number, Integer[] token ) {
+        Integer[] fn = null;
+        assert token.length > 0;
+        if ( number < 0 ) {
+            throw new IllegalArgumentException ( "number must be non-negative" );
+        } else if ( token.length == 1 ) {
+            int s = token[0].intValue();
+            switch ( s ) {
+            case (int) '1':
+                {
+                    fn = formatNumberAsDecimal ( number, (int) '1', 1 );
+                    break;
+                }
+            case (int) 'W':
+            case (int) 'w':
+                {
+                    fn = formatNumberAsWord ( number, ( s == (int) 'W' ) ? Character.UPPERCASE_LETTER : Character.LOWERCASE_LETTER );
+                    break;
+                }
+            case (int) 'A': // handled as numeric sequence
+            case (int) 'a': // handled as numeric sequence
+            case (int) 'I': // handled as numeric special
+            case (int) 'i': // handled as numeric special
+            default:
+                {
+                    if ( isStartOfDecimalSequence ( s ) ) {
+                        fn = formatNumberAsDecimal ( number, s, 1 );
+                    } else if ( isStartOfAlphabeticSequence ( s ) ) {
+                        fn = formatNumberAsSequence ( number, s, getSequenceBase ( s ), null );
+                    } else if ( isStartOfNumericSpecial ( s ) ) {
+                        fn = formatNumberAsSpecial ( number, s );
+                    } else {
+                        fn = null;
+                    }
+                    break;
+                }
+            }
+        } else if ( ( token.length == 2 ) && ( token[0] == (int) 'W' ) && ( token[1] == (int) 'w' ) ) {
+            fn = formatNumberAsWord ( number, Character.TITLECASE_LETTER );
+        } else if ( isPaddedOne ( token ) ) {
+            int s = token [ token.length - 1 ].intValue();
+            fn = formatNumberAsDecimal ( number, s, token.length );
+        } else {
+            throw new IllegalArgumentException ( "invalid format token: \"" + CharUtilities.fromUTF32 ( token ) + "\"" );
+        }
+        if ( fn == null ) {
+            fn = formatNumber ( number, DEFAULT_TOKEN );
+        }
+        assert fn != null;
+        return fn;
+    }
+
+    /**
+     * Format NUMBER as decimal using characters denoting digits that start at ONE,
+     * adding one or more (zero) padding characters as needed to fill out field WIDTH.
+     * @param number to be formatted
+     * @param one unicode scalar value denoting numeric value 1
+     * @param width non-negative integer denoting field width of number, possible including padding
+     * @return formatted number as array of unicode scalars
+     */
+    private Integer[] formatNumberAsDecimal ( long number, int one, int width ) {
+        assert Character.getNumericValue ( one ) == 1;
+        assert Character.getNumericValue ( one - 1 ) == 0;
+        assert Character.getNumericValue ( one + 8 ) == 9;
+        List<Integer> sl = new ArrayList<Integer>();
+        int zero = one - 1;
+        while ( number > 0 ) {
+            long digit = number % 10;
+            sl.add ( 0, zero + (int) digit );
+            number = number / 10;
+        }
+        while ( width > sl.size() ) {
+            sl.add ( 0, zero );
+        }
+        if ( ( groupingSize != 0 ) && ( groupingSeparator != 0 ) ) {
+            sl = performGrouping ( sl, groupingSize, groupingSeparator );
+        }
+        return sl.toArray ( new Integer [ sl.size() ] );
+    }
+
+    private static List<Integer> performGrouping ( List<Integer> sl, int groupingSize, int groupingSeparator ) {
+        assert groupingSize > 0;
+        assert groupingSeparator != 0;
+        if ( sl.size() > groupingSize ) {
+            List<Integer> gl = new ArrayList<Integer>();
+            for ( int i = 0, n = sl.size(), g = 0; i < n; i++ ) {
+                int k = n - i - 1;
+                if ( g == groupingSize ) {
+                    gl.add ( 0, groupingSeparator );
+                    g = 1;
+                } else {
+                    g++;
+                }
+                gl.add ( 0, sl.get ( k ) );
+            }
+            return gl;
+        } else {
+            return sl;
+        }
+    }
+
+
+    /**
+     * Format NUMBER as using sequence of characters that start at ONE, and
+     * having BASE radix.
+     * @param number to be formatted
+     * @param one unicode scalar value denoting start of sequence (numeric value 1)
+     * @param base number of elements in sequence
+     * @param map if non-null, then maps sequences indices to unicode scalars
+     * @return formatted number as array of unicode scalars
+     */
+    private Integer[] formatNumberAsSequence ( long number, int one, int base, int[] map ) {
+        assert base > 1;
+        assert ( map == null ) || ( map.length >= base );
+        List<Integer> sl = new ArrayList<Integer>();
+        if ( number == 0 ) {
+            return null;
+        } else {
+            long n = number;
+            while ( n > 0 ) {
+                int d = (int) ( ( n - 1 ) % (long) base );
+                int s = ( map != null ) ? map [ d ] : ( one + d );
+                sl.add ( 0, s );
+                n = ( n - 1 ) / base;
+            }
+            return sl.toArray ( new Integer [ sl.size() ] );
+        }
+    }
+
+    /**
+     * Format NUMBER as using special system that starts at ONE.
+     * @param number to be formatted
+     * @param one unicode scalar value denoting start of system (numeric value 1)
+     * @return formatted number as array of unicode scalars
+     */
+    private Integer[] formatNumberAsSpecial ( long number, int one ) {
+        SpecialNumberFormatter f = getSpecialFormatter ( one, letterValue, features, language, country );
+        if ( f != null ) {
+            return f.format ( number, one, letterValue, features, language, country );
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Format NUMBER as word according to TYPE, which must be either
+     * Character.UPPERCASE_LETTER, Character.LOWERCASE_LETTER, or 
+     * Character.TITLECASE_LETTER. Makes use of this.language to
+     * determine language of word.
+     * @param number to be formatted
+     * @param caseType unicode character type for case conversion
+     * @return formatted number as array of unicode scalars
+     */
+    private Integer[] formatNumberAsWord ( long number, int caseType ) {
+        SpecialNumberFormatter f = null;
+        if ( isLanguage ( "eng" ) ) {
+            f = new EnglishNumberAsWordFormatter ( caseType );
+        } else if ( isLanguage ( "spa" ) ) {
+            f = new SpanishNumberAsWordFormatter ( caseType );
+        } else if ( isLanguage ( "fra" ) ) {
+            f = new FrenchNumberAsWordFormatter ( caseType );
+        } else {
+            f = new EnglishNumberAsWordFormatter ( caseType );
+        }
+        return f.format ( number, 0, letterValue, features, language, country );
+    }
+
+    private boolean isLanguage ( String iso3Code ) {
+        if ( language == null ) {
+            return false;
+        } else if ( language.equals ( iso3Code ) ) {
+            return true;
+        } else {
+            return isSameLanguage ( iso3Code, language );
+        }
+    }
+
+    private static String[][] equivalentLanguages = {
+        { "eng", "en" },
+        { "fra", "fre", "fr" },
+        { "spa", "es" },
+    };
+
+    private static boolean isSameLanguage ( String i3c, String lc ) {
+        for ( String[] el : equivalentLanguages ) {
+            assert el.length >= 2;
+            if ( el[0].equals ( i3c ) ) {
+                for ( int i = 0, n = el.length; i < n; i++ ) {
+                    if ( el[i].equals ( lc ) ) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        }
+        return false;
+    }
+
+    private static boolean hasFeature ( String features, String feature ) {
+        if ( features != null ) {
+            assert feature != null;
+            assert feature.length() != 0;
+            String[] fa = features.split(",");
+            for ( String f : fa ) {
+                String[] fp = f.split("=");
+                assert fp.length > 0;
+                String   fn = fp[0];
+                String   fv = ( fp.length > 1 ) ? fp[1] : "";
+                if ( fn.equals ( feature ) ) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /* not yet used
+    private static String getFeatureValue ( String features, String feature ) {
+        if ( features != null ) {
+            assert feature != null;
+            assert feature.length() != 0;
+            String[] fa = features.split(",");
+            for ( String f : fa ) {
+                String[] fp = f.split("=");
+                assert fp.length > 0;
+                String   fn = fp[0];
+                String   fv = ( fp.length > 1 ) ? fp[1] : "";
+                if ( fn.equals ( feature ) ) {
+                    return fv;
+                }
+            }
+        }
+        return "";
+    }
+    */
+
+    private static void appendScalars ( List<Integer> scalars, Integer[] sa ) {
+        for ( Integer s : sa ) {
+            scalars.add ( s );
+        }
+    }
+
+    private static String scalarsToString ( List<Integer> scalars ) {
+        Integer[] sa = scalars.toArray ( new Integer [ scalars.size() ] );
+        return CharUtilities.fromUTF32 ( sa );
+    }
+
+    private static boolean isPaddedOne ( Integer[] token ) {
+        if ( getDecimalValue ( token [ token.length - 1 ] ) != 1 ) {
+            return false;
+        } else {
+            for ( int i = 0, n = token.length - 1; i < n; i++ ) {
+                if ( getDecimalValue ( token [ i ] ) != 0 ) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private static int getDecimalValue ( Integer scalar ) {
+        int s = scalar.intValue();
+        if ( Character.getType ( s ) == Character.DECIMAL_DIGIT_NUMBER ) {
+            return Character.getNumericValue ( s );
+        } else {
+            return -1;
+        }
+    }
+
+    private static boolean isStartOfDecimalSequence ( int s ) {
+        return ( Character.getNumericValue ( s ) == 1 )
+            && ( Character.getNumericValue ( s - 1 ) == 0 )
+            && ( Character.getNumericValue ( s + 8 ) == 9 );
+    }
+
+    private static int[][] supportedAlphabeticSequences = {
+        { 'A', 26 },            // A...Z
+        { 'a', 26 },            // a...z
+    };
+
+    private static boolean isStartOfAlphabeticSequence ( int s ) {
+        for ( int[] ss : supportedAlphabeticSequences ) {
+            assert ss.length >= 2;
+            if ( ss[0] == s ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static int getSequenceBase ( int s ) {
+        for ( int[] ss : supportedAlphabeticSequences ) {
+            assert ss.length >= 2;
+            if ( ss[0] == s ) {
+                return ss[1];
+            }
+        }
+        return 0;
+    }
+
+    private static int[][] supportedSpecials = {
+        { 'I' },                // latin - uppercase roman numerals
+        { 'i' },                // latin - lowercase roman numerals
+        { '\u0391' },           // greek - uppercase isopsephry numerals
+        { '\u03B1' },           // greek - lowercase isopsephry numerals
+        { '\u05D0' },           // hebrew - gematria numerals
+        { '\u0623' },           // arabic - abjadi numberals
+        { '\u0627' },           // arabic - either abjadi or hijai alphabetic sequence
+        { '\u0E01' },           // thai - default alphabetic sequence
+        { '\u3042' },           // kana - hiragana (gojuon) - default alphabetic sequence
+        { '\u3044' },           // kana - hiragana (iroha)
+        { '\u30A2' },           // kana - katakana (gojuon) - default alphabetic sequence
+        { '\u30A4' },           // kana - katakana (iroha)
+    };
+
+    private static boolean isStartOfNumericSpecial ( int s ) {
+        for ( int[] ss : supportedSpecials ) {
+            assert ss.length >= 1;
+            if ( ss[0] == s ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private SpecialNumberFormatter getSpecialFormatter ( int one, int letterValue, String features, String language, String country ) {
+        if ( one == (int) 'I' ) {
+            return new RomanNumeralsFormatter();
+        } else if ( one == (int) 'i' ) {
+            return new RomanNumeralsFormatter();
+        } else if ( one == (int) '\u0391' ) {
+            return new IsopsephryNumeralsFormatter();
+        } else if ( one == (int) '\u03B1' ) {
+            return new IsopsephryNumeralsFormatter();
+        } else if ( one == (int) '\u05D0' ) {
+            return new GematriaNumeralsFormatter();
+        } else if ( one == (int) '\u0623' ) {
+            return new ArabicNumeralsFormatter();
+        } else if ( one == (int) '\u0627' ) {
+            return new ArabicNumeralsFormatter();
+        } else if ( one == (int) '\u0E01' ) {
+            return new ThaiNumeralsFormatter();
+        } else if ( one == (int) '\u3042' ) {
+            return new KanaNumeralsFormatter();
+        } else if ( one == (int) '\u3044' ) {
+            return new KanaNumeralsFormatter();
+        } else if ( one == (int) '\u30A2' ) {
+            return new KanaNumeralsFormatter();
+        } else if ( one == (int) '\u30A4' ) {
+            return new KanaNumeralsFormatter();
+        } else {
+            return null;
+        }
+    }
+
+    private static Integer[] toUpperCase ( Integer[] sa ) {
+        assert sa != null;
+        for ( int i = 0, n = sa.length; i < n; i++ ) {
+            Integer s = sa [ i ];
+            sa [ i ] = Character.toUpperCase ( s );
+        }
+        return sa;
+    }
+
+    private static Integer[] toLowerCase ( Integer[] sa ) {
+        assert sa != null;
+        for ( int i = 0, n = sa.length; i < n; i++ ) {
+            Integer s = sa [ i ];
+            sa [ i ] = Character.toLowerCase ( s );
+        }
+        return sa;
+    }
+
+    /* not yet used
+    private static Integer[] toTitleCase ( Integer[] sa ) {
+        assert sa != null;
+        if ( sa.length > 0 ) {
+            sa [ 0 ] = Character.toTitleCase ( sa [ 0 ] );
+        }
+        return sa;
+    }
+    */
+
+    private static List<String> convertWordCase ( List<String> words, int caseType ) {
+        List<String> wl = new ArrayList<String>();
+        for ( String w : words ) {
+            wl.add ( convertWordCase ( w, caseType ) );
+        }
+        return wl;
+    }
+
+    private static String convertWordCase ( String word, int caseType ) {
+        if ( caseType == Character.UPPERCASE_LETTER ) {
+            return word.toUpperCase();
+        } else if ( caseType == Character.LOWERCASE_LETTER ) {
+            return word.toLowerCase();
+        } else if ( caseType == Character.TITLECASE_LETTER ) {
+            StringBuffer sb = new StringBuffer();
+            for ( int i = 0, n = word.length(); i < n; i++ ) {
+                String s = word.substring ( i, i + 1 );
+                if ( i == 0 ) {
+                    sb.append ( s.toUpperCase() );
+                } else {
+                    sb.append ( s.toLowerCase() );
+                }
+            }
+            return sb.toString();
+        } else {
+            return word;
+        }
+    }
+
+    private static String joinWords ( List<String> words, String separator ) {
+        StringBuffer sb = new StringBuffer();
+        for ( String w : words ) {
+            if ( sb.length() > 0 ) {
+                sb.append ( separator );
+            }
+            sb.append ( w );
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Special number formatter.
+     */
+    interface SpecialNumberFormatter {
+        /**
+         * Format number with special numeral system.
+         * @param number to be formatted
+         * @param one unicode scalar value denoting numeric value 1
+         * @param letterValue letter value (must be one of the above letter value enumeration values)
+         * @param features features (feature sub-parameters)
+         * @param language denotes applicable language
+         * @param country denotes applicable country
+         * @return formatted number as array of unicode scalars
+         */
+        Integer[] format ( long number, int one, int letterValue, String features, String language, String country );
+    }
+
+    /**
+     * English Word Numerals
+     */
+    private static String[] englishWordOnes = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" };
+    private static String[] englishWordTeens = { "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
+    private static String[] englishWordTens = { "", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" };
+    private static String[] englishWordOthers = { "hundred", "thousand", "million", "billion" };
+    private static String[] englishWordOnesOrd = { "none", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth" };
+    private static String[] englishWordTeensOrd = { "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth" };
+    private static String[] englishWordTensOrd = { "", "tenth", "twentieth", "thirtieth", "fortieth", "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetith" };
+    private static String[] englishWordOthersOrd = { "hundredth", "thousandth", "millionth", "billionth" };
+    private static class EnglishNumberAsWordFormatter implements SpecialNumberFormatter {
+        private int caseType = Character.UPPERCASE_LETTER;
+        EnglishNumberAsWordFormatter ( int caseType ) {
+            this.caseType = caseType;
+        }
+        @Override
+        public Integer[] format ( long number, int one, int letterValue, String features, String language, String country ) {
+            List<String> wl = new ArrayList<String>();
+            if ( number >= 1000000000000L ) {
+                return null;
+            } else {
+                boolean ordinal = hasFeature ( features, "ordinal" );
+                if ( number == 0 ) {
+                    wl.add ( englishWordOnes [ 0 ] );
+                } else if ( ordinal && ( number < 10 ) ) {
+                    wl.add ( englishWordOnesOrd [ (int) number ] );
+                } else {
+                    int ones = (int) ( number % 1000 );
+                    int thousands = (int) ( ( number / 1000 ) % 1000 );
+                    int millions = (int) ( ( number / 1000000 ) % 1000 );
+                    int billions = (int) ( ( number / 1000000000 ) % 1000 );
+                    if ( billions > 0 ) {
+                        wl = formatOnesInThousand ( wl, billions );
+                        if ( ordinal && ( ( number % 1000000000 ) == 0 ) ) {
+                            wl.add ( englishWordOthersOrd[3] );
+                        } else {
+                            wl.add ( englishWordOthers[3] );
+                        }
+                    }
+                    if ( millions > 0 ) {
+                        wl = formatOnesInThousand ( wl, millions );
+                        if ( ordinal && ( ( number % 1000000 ) == 0 ) ) {
+                            wl.add ( englishWordOthersOrd[2] );
+                        } else {
+                            wl.add ( englishWordOthers[2] );
+                        }
+                    }
+                    if ( thousands > 0 ) {
+                        wl = formatOnesInThousand ( wl, thousands );
+                        if ( ordinal && ( ( number % 1000 ) == 0 ) ) {
+                            wl.add ( englishWordOthersOrd[1] );
+                        } else {
+                            wl.add ( englishWordOthers[1] );
+                        }
+                    }
+                    if ( ones > 0 ) {
+                        wl = formatOnesInThousand ( wl, ones, ordinal );
+                    }
+                }
+                wl = convertWordCase ( wl, caseType );
+                return CharUtilities.toUTF32 ( joinWords ( wl, " " ), 0, true );
+            }
+        }
+        private List<String> formatOnesInThousand ( List<String> wl, int number ) {
+            return formatOnesInThousand ( wl, number, false );
+        }
+        private List<String> formatOnesInThousand ( List<String> wl, int number, boolean ordinal ) {
+            assert number < 1000;
+            int ones = number % 10;
+            int tens = ( number / 10 ) % 10;
+            int hundreds = ( number / 100 ) % 10;
+            if ( hundreds > 0 ) {
+                wl.add ( englishWordOnes [ hundreds ] );
+                if ( ordinal && ( ( number % 100 ) == 0 ) ) {
+                    wl.add ( englishWordOthersOrd[0] );
+                } else {
+                    wl.add ( englishWordOthers[0] );
+                }
+            }
+            if ( tens > 0 ) {
+                if ( tens == 1 ) {
+                    if ( ordinal ) {
+                        wl.add ( englishWordTeensOrd [ ones ] );
+                    } else {
+                        wl.add ( englishWordTeens [ ones ] );
+                    }
+                } else {
+                    if ( ordinal && ( ones == 0 ) ) {
+                        wl.add ( englishWordTensOrd [ tens ] );
+                    } else {
+                        wl.add ( englishWordTens [ tens ] );
+                    }
+                    if ( ones > 0 ) {
+                        if ( ordinal ) {
+                            wl.add ( englishWordOnesOrd [ ones ] );
+                        } else {
+                            wl.add ( englishWordOnes [ ones ] );
+                        }
+                    }
+                }
+            } else if ( ones > 0 ) {
+                if ( ordinal ) {
+                    wl.add ( englishWordOnesOrd [ ones ] );
+                } else {
+                    wl.add ( englishWordOnes [ ones ] );
+                }
+            }
+            return wl;
+        }
+    }
+
+    /**
+     * French Word Numerals
+     */
+    private static String[] frenchWordOnes = { "z\u00e9ro", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf" };
+    private static String[] frenchWordTeens = { "dix", "onze", "douze", "treize", "quatorze", "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf" };
+    private static String[] frenchWordTens = { "", "dix", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante-dix", "quatre-vingt", "quatre-vingt-dix" };
+    private static String[] frenchWordOthers = { "cent", "cents", "mille", "million", "millions", "milliard", "milliards" };
+    private static String[] frenchWordOnesOrdMale = { "premier", "deuxi\u00e8me", "troisi\u00e8me", "quatri\u00e8me", "cinqui\u00e8me", "sixi\u00e8me", "septi\u00e8me", "huiti\u00e8me", "neuvi\u00e8me", "dixi\u00e8me" };
+    private static String[] frenchWordOnesOrdFemale = { "premi\u00e8re", "deuxi\u00e8me", "troisi\u00e8me", "quatri\u00e8me", "cinqui\u00e8me", "sixi\u00e8me", "septi\u00e8me", "huiti\u00e8me", "neuvi\u00e8me", "dixi\u00e8me" };
+    private static class FrenchNumberAsWordFormatter implements SpecialNumberFormatter {
+        private int caseType = Character.UPPERCASE_LETTER;
+        FrenchNumberAsWordFormatter ( int caseType ) {
+            this.caseType = caseType;
+        }
+        @Override
+        public Integer[] format ( long number, int one, int letterValue, String features, String language, String country ) {
+            List<String> wl = new ArrayList<String>();
+            if ( number >= 1000000000000L ) {
+                return null;
+            } else {
+                boolean ordinal = hasFeature ( features, "ordinal" );
+                if ( number == 0 ) {
+                    wl.add ( frenchWordOnes [ 0 ] );
+                } else if ( ordinal && ( number <= 10 ) ) {
+                    boolean female = hasFeature ( features, "female" );
+                    if ( female ) {
+                        wl.add ( frenchWordOnesOrdFemale [ (int) number ] );
+                    } else {
+                        wl.add ( frenchWordOnesOrdMale [ (int) number ] );
+                    }
+                } else {
+                    int ones = (int) ( number % 1000 );
+                    int thousands = (int) ( ( number / 1000 ) % 1000 );
+                    int millions = (int) ( ( number / 1000000 ) % 1000 );
+                    int billions = (int) ( ( number / 1000000000 ) % 1000 );
+                    if ( billions > 0 ) {
+                        wl = formatOnesInThousand ( wl, billions );
+                        if ( billions == 1 ) {
+                            wl.add ( frenchWordOthers[5] );
+                        } else {
+                            wl.add ( frenchWordOthers[6] );
+                        }
+                    }
+                    if ( millions > 0 ) {
+                        wl = formatOnesInThousand ( wl, millions );
+                        if ( millions == 1 ) {
+                            wl.add ( frenchWordOthers[3] );
+                        } else {
+                            wl.add ( frenchWordOthers[4] );
+                        }
+                    }
+                    if ( thousands > 0 ) {
+                        if ( thousands > 1 ) {
+                            wl = formatOnesInThousand ( wl, thousands );
+                        }
+                        wl.add ( frenchWordOthers[2] );
+                    }
+                    if ( ones > 0 ) {
+                        wl = formatOnesInThousand ( wl, ones );
+                    }
+                }
+                wl = convertWordCase ( wl, caseType );
+                return CharUtilities.toUTF32 ( joinWords ( wl, " " ), 0, true );
+            }
+        }
+        private List<String> formatOnesInThousand ( List<String> wl, int number ) {
+            assert number < 1000;
+            int ones = number % 10;
+            int tens = ( number / 10 ) % 10;
+            int hundreds = ( number / 100 ) % 10;
+            if ( hundreds > 0 ) {
+                if ( hundreds > 1 ) {
+                    wl.add ( frenchWordOnes [ hundreds ] );
+                }
+                if ( ( hundreds > 1 ) && ( tens == 0 ) && ( ones == 0 ) ) {
+                    wl.add ( frenchWordOthers[1] );
+                } else {
+                    wl.add ( frenchWordOthers[0] );
+                }
+            }
+            if ( tens > 0 ) {
+                if ( tens == 1 ) {
+                    wl.add ( frenchWordTeens [ ones ] );
+                } else if ( tens < 7 ) {
+                    if ( ones == 1 ) {
+                        wl.add ( frenchWordTens [ tens ] );
+                        wl.add ( "et" );
+                        wl.add ( frenchWordOnes [ ones ] );
+                    } else {
+                        StringBuffer sb = new StringBuffer();
+                        sb.append ( frenchWordTens [ tens ] );
+                        if ( ones > 0 ) {
+                            sb.append ( '-' );
+                            sb.append ( frenchWordOnes [ ones ] );
+                        }
+                        wl.add ( sb.toString() );
+                    }
+                } else if ( tens == 7 ) {
+                    if ( ones == 1 ) {
+                        wl.add ( frenchWordTens [ 6 ] );
+                        wl.add ( "et" );
+                        wl.add ( frenchWordTeens [ ones ] );
+                    } else {
+                        StringBuffer sb = new StringBuffer();
+                        sb.append ( frenchWordTens [ 6 ] );
+                        sb.append ( '-' );
+                        sb.append ( frenchWordTeens [ ones ] );
+                        wl.add ( sb.toString() );
+                    }
+                } else if ( tens == 8 ) {
+                    StringBuffer sb = new StringBuffer();
+                    sb.append ( frenchWordTens [ tens ] );
+                    if ( ones > 0 ) {
+                        sb.append ( '-' );
+                        sb.append ( frenchWordOnes [ ones ] );
+                    } else {
+                        sb.append ( 's' );
+                    }
+                    wl.add ( sb.toString() );
+                } else if ( tens == 9 ) {
+                    StringBuffer sb = new StringBuffer();
+                    sb.append ( frenchWordTens [ 8 ] );
+                    sb.append ( '-' );
+                    sb.append ( frenchWordTeens [ ones ] );
+                    wl.add ( sb.toString() );
+                }
+            } else if ( ones > 0 ) {
+                wl.add ( frenchWordOnes [ ones ] );
+            }
+            return wl;
+        }
+    }
+
+    /**
+     * Spanish Word Numerals
+     */
+    private static String[] spanishWordOnes = { "cero", "uno", "dos", "tres", "cuatro", "cinco", "seise", "siete", "ocho", "nueve" };
+    private static String[] spanishWordTeens = { "diez", "once", "doce", "trece", "catorce", "quince", "diecis\u00e9is", "diecisiete", "dieciocho", "diecinueve" };
+    private static String[] spanishWordTweens = { "veinte", "veintiuno", "veintid\u00f3s", "veintitr\u00e9s", "veinticuatro", "veinticinco", "veintis\u00e9is", "veintisiete", "veintiocho", "veintinueve" };
+    private static String[] spanishWordTens = { "", "diez", "veinte", "treinta", "cuarenta", "cincuenta", "sesenta", "setenta", "ochenta", "noventa" };
+    private static String[] spanishWordHundreds = { "", "ciento", "doscientos", "trescientos", "cuatrocientos", "quinientos", "seiscientos", "setecientos", "ochocientos", "novecientos" };
+    private static String[] spanishWordOthers = { "un", "cien", "mil", "mill\u00f3n", "millones" };
+    private static String[] spanishWordOnesOrdMale = { "ninguno", "primero", "segundo", "tercero", "cuarto", "quinto", "sexto", "s\u00e9ptimo", "octavo", "novento", "d\u00e9cimo" };
+    private static String[] spanishWordOnesOrdFemale = { "ninguna", "primera", "segunda", "tercera", "cuarta", "quinta", "sexta", "s\u00e9ptima", "octava", "noventa", "d\u00e9cima" };
+    private static class SpanishNumberAsWordFormatter implements SpecialNumberFormatter {
+        private int caseType = Character.UPPERCASE_LETTER;
+        SpanishNumberAsWordFormatter ( int caseType ) {
+            this.caseType = caseType;
+        }
+        @Override
+        public Integer[] format ( long number, int one, int letterValue, String features, String language, String country ) {
+            List<String> wl = new ArrayList<String>();
+            if ( number >= 1000000000000L ) {
+                return null;
+            } else {
+                boolean ordinal = hasFeature ( features, "ordinal" );
+                if ( number == 0 ) {
+                    wl.add ( spanishWordOnes [ 0 ] );
+                } else if ( ordinal && ( number <= 10 ) ) {
+                    boolean female = hasFeature ( features, "female" );
+                    if ( female ) {
+                        wl.add ( spanishWordOnesOrdFemale [ (int) number ] );
+                    } else {
+                        wl.add ( spanishWordOnesOrdMale [ (int) number ] );
+                    }
+                } else {
+                    int ones = (int) ( number % 1000 );
+                    int thousands = (int) ( ( number / 1000 ) % 1000 );
+                    int millions = (int) ( ( number / 1000000 ) % 1000 );
+                    int billions = (int) ( ( number / 1000000000 ) % 1000 );
+                    if ( billions > 0 ) {
+                        if ( billions > 1 ) {
+                            wl = formatOnesInThousand ( wl, billions );
+                        }
+                        wl.add ( spanishWordOthers[2] );
+                        wl.add ( spanishWordOthers[4] );
+                    }
+                    if ( millions > 0 ) {
+                        if ( millions == 1 ) {
+                            wl.add ( spanishWordOthers[0] );
+                        } else {
+                            wl = formatOnesInThousand ( wl, millions );
+                        }
+                        if ( millions > 1 ) {
+                            wl.add ( spanishWordOthers[4] );
+                        } else {
+                            wl.add ( spanishWordOthers[3] );
+                        }
+                    }
+                    if ( thousands > 0 ) {
+                        if ( thousands > 1 ) {
+                            wl = formatOnesInThousand ( wl, thousands );
+                        }
+                        wl.add ( spanishWordOthers[2] );
+                    }
+                    if ( ones > 0 ) {
+                        wl = formatOnesInThousand ( wl, ones );
+                    }
+                }
+                wl = convertWordCase ( wl, caseType );
+                return CharUtilities.toUTF32 ( joinWords ( wl, " " ), 0, true );
+            }
+        }
+        private List<String> formatOnesInThousand ( List<String> wl, int number ) {
+            assert number < 1000;
+            int ones = number % 10;
+            int tens = ( number / 10 ) % 10;
+            int hundreds = ( number / 100 ) % 10;
+            if ( hundreds > 0 ) {
+                if ( ( hundreds == 1 ) && ( tens == 0 ) && ( ones == 0 ) ) {
+                    wl.add ( spanishWordOthers[1] );
+                } else {
+                    wl.add ( spanishWordHundreds [ hundreds ] );
+                }
+            }
+            if ( tens > 0 ) {
+                if ( tens == 1 ) {
+                    wl.add ( spanishWordTeens [ ones ] );
+                } else if ( tens == 2 ) {
+                    wl.add ( spanishWordTweens [ ones ] );
+                } else {
+                    wl.add ( spanishWordTens [ tens ] );
+                    if ( ones > 0 ) {
+                        wl.add ( "y" );
+                        wl.add ( spanishWordOnes [ ones ] );
+                    }
+                }
+            } else if ( ones > 0 ) {
+                wl.add ( spanishWordOnes [ ones ] );
+            }
+            return wl;
+        }
+    }
+
+    /**
+     * Roman (Latin) Numerals
+     */
+    private static int[] romanMapping = {
+        100000,
+        90000,
+        50000,
+        40000,
+        10000,
+        9000,
+        5000,
+        4000,
+        1000,
+        900,
+        500,
+        400,
+        100,
+        90,
+        50,
+        40,
+        10,
+        9,
+        8,
+        7,
+        6,
+        5,
+        4,
+        3,
+        2,
+        1
+    };
+    private static String[] romanStandardForms = {
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        "m",
+        "cm",
+        "d",
+        "cd",
+        "c",
+        "xc",
+        "l",
+        "xl",
+        "x",
+        "ix",
+        null,
+        null,
+        null,
+        "v",
+        "iv",
+        null,
+        null,
+        "i"
+    };
+    private static String[] romanLargeForms = {
+        "\u2188",
+        "\u2182\u2188",
+        "\u2187",
+        "\u2182\u2187",
+        "\u2182",
+        "\u2180\u2182",
+        "\u2181",
+        "\u2180\u2181",
+        "m",
+        "cm",
+        "d",
+        "cd",
+        "c",
+        "xc",
+        "l",
+        "xl",
+        "x",
+        "ix",
+        null,
+        null,
+        null,
+        "v",
+        "iv",
+        null,
+        null,
+        "i"
+    };
+    private static String[] romanNumberForms = {
+        "\u2188",
+        "\u2182\u2188",
+        "\u2187",
+        "\u2182\u2187",
+        "\u2182",
+        "\u2180\u2182",
+        "\u2181",
+        "\u2180\u2181",
+        "\u216F",
+        "\u216D\u216F",
+        "\u216E",
+        "\u216D\u216E",
+        "\u216D",
+        "\u2169\u216D",
+        "\u216C",
+        "\u2169\u216C",
+        "\u2169",
+        "\u2168",
+        "\u2167",
+        "\u2166",
+        "\u2165",
+        "\u2164",
+        "\u2163",
+        "\u2162",
+        "\u2161",
+        "\u2160"
+    };
+    private static class RomanNumeralsFormatter implements SpecialNumberFormatter {
+        @Override
+        public Integer[] format ( long number, int one, int letterValue, String features, String language, String country ) {
+            List<Integer> sl = new ArrayList<Integer>();
+            if ( number == 0 ) {
+                return null;
+            } else {
+                String[] forms;
+                int maxNumber;
+                if ( hasFeature ( features, "unicode-number-forms" ) ) {
+                    forms = romanNumberForms;
+                    maxNumber = 199999;
+                } else if ( hasFeature ( features, "large" ) ) {
+                    forms = romanLargeForms;
+                    maxNumber = 199999;
+                } else {
+                    forms = romanStandardForms;
+                    maxNumber = 4999;
+                }
+                if ( number > maxNumber ) {
+                    return null;
+                } else {
+                    while ( number > 0 ) {
+                        for ( int i = 0, n = romanMapping.length; i < n; i++ ) {
+                            int d = romanMapping [ i ];
+                            if ( ( number >= d ) && ( forms [ i ] != null ) ) {
+                                appendScalars ( sl, CharUtilities.toUTF32 ( forms [ i ], 0, true ) );
+                                number = number - d;
+                                break;
+                            }
+                        }
+                    }
+                    if ( one == (int) 'I' ) {
+                        return toUpperCase ( sl.toArray ( new Integer [ sl.size() ] ) );
+                    } else if ( one == (int) 'i' ) {
+                        return toLowerCase ( sl.toArray ( new Integer [ sl.size() ] ) );
+                    } else {
+                        return null;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Isopsephry (Greek) Numerals
+     */
+    private static class IsopsephryNumeralsFormatter implements SpecialNumberFormatter {
+        @Override
+        public Integer[] format ( long number, int one, int letterValue, String features, String language, String country ) {
+            return null;
+        }
+    }
+
+    /**
+     * Gematria (Hebrew) Numerals
+     */
+    private static int[] hebrewGematriaAlphabeticMap = {
+        // ones
+        0x05D0, // ALEF
+        0x05D1, // BET
+        0x05D2, // GIMEL
+        0x05D3, // DALET
+        0x05D4, // HE
+        0x05D5, // VAV
+        0x05D6, // ZAYIN
+        0x05D7, // HET
+        0x05D8, // TET
+        // tens
+        0x05D9, // YOD
+        0x05DB, // KAF
+        0x05DC, // LAMED
+        0x05DE, // MEM
+        0x05E0, // NUN
+        0x05E1, // SAMEKH
+        0x05E2, // AYIN
+        0x05E4, // PE
+        0x05E6, // TSADHI
+        // hundreds
+        0x05E7, // QOF
+        0x05E8, // RESH
+        0x05E9, // SHIN
+        0x05EA, // TAV
+        0x05DA, // FINAL KAF
+        0x05DD, // FINAL MEM
+        0x05DF, // FINAL NUN
+        0x05E3, // FINAL PE
+        0x05E5, // FINAL TSADHI
+    };
+    private class GematriaNumeralsFormatter implements SpecialNumberFormatter {
+        @Override
+        public Integer[] format ( long number, int one, int letterValue, String features, String language, String country ) {
+            if ( one == 0x05D0 ) {
+                if ( letterValue == LETTER_VALUE_ALPHABETIC ) {
+                    return formatNumberAsSequence ( number, one, hebrewGematriaAlphabeticMap.length, hebrewGematriaAlphabeticMap );
+                } else if ( letterValue == LETTER_VALUE_TRADITIONAL ) {
+                    if ( ( number == 0 ) || ( number > 1999 ) ) {
+                        return null;
+                    } else {
+                        return formatAsGematriaNumber ( number, features, language, country );
+                    }
+                } else {
+                    return null;
+                }
+            } else {
+                return null;
+            }
+        }
+        private Integer[] formatAsGematriaNumber ( long number, String features, String language, String country ) {
+            List<Integer> sl = new ArrayList<Integer>();
+            assert hebrewGematriaAlphabeticMap.length == 27;
+            assert hebrewGematriaAlphabeticMap[0] == 0x05D0;  // ALEF
+            assert hebrewGematriaAlphabeticMap[21] == 0x05EA; // TAV
+            assert number != 0;
+            assert number < 2000;
+            int[] map = hebrewGematriaAlphabeticMap;
+            int thousands = (int) ( ( number / 1000 ) % 10 );
+            int hundreds = (int) ( ( number / 100 ) % 10 );
+            int tens = (int) ( ( number / 10 ) % 10 );
+            int ones = (int) ( ( number / 1 ) % 10 );
+            if ( thousands > 0 ) {
+                sl.add ( map [ 0 + ( thousands - 1 ) ] );
+                sl.add ( 0x05F3 );
+            }
+            if ( hundreds > 0 ) {
+                assert hundreds < 10;
+                if ( hundreds < 5 ) {
+                    sl.add ( map [ 18 + ( hundreds - 1 ) ] );
+                } else if ( hundreds < 9 ) {
+                    sl.add ( map [ 18 + ( 4 - 1 ) ] );
+                    sl.add ( 0x05F4 );
+                    sl.add ( map [ 18 + ( hundreds - 5 ) ] );
+                } else if ( hundreds == 9 ) {
+                    sl.add ( map [ 18 + ( 4 - 1 ) ] );
+                    sl.add ( map [ 18 + ( 4 - 1 ) ] );
+                    sl.add ( 0x05F4 );
+                    sl.add ( map [ 18 + ( hundreds - 9 ) ] );
+                }
+            }
+            if ( number == 15 ) {
+                sl.add ( map [ 9 - 1] );
+                sl.add ( 0x05F4 );
+                sl.add ( map [ 6 - 1] );
+            } else if ( number == 16 ) {
+                sl.add ( map [ 9 - 1 ] );
+                sl.add ( 0x05F4 );
+                sl.add ( map [ 7 - 1 ] );
+            } else {
+                if ( tens > 0 ) {
+                    assert tens < 10;
+                    sl.add ( map [ 9 + ( tens - 1 ) ] );
+                }
+                if ( ones > 0 ) {
+                    assert ones < 10;
+                    sl.add ( map [ 0 + ( ones - 1 ) ] );
+                }
+            }
+            return sl.toArray ( new Integer [ sl.size() ] );
+        }
+    }
+
+    /**
+     * Arabic Numerals
+     */
+    private static int[] arabicAbjadiAlphabeticMap = {
+        // ones
+        0x0623, // ALEF WITH HAMZA ABOVE
+        0x0628, // BEH
+        0x062C, // JEEM
+        0x062F, // DAL
+        0x0647, // HEH
+        0x0648, // WAW
+        0x0632, // ZAIN
+        0x062D, // HAH
+        0x0637, // TAH
+        // tens
+        0x0649, // ALEF MAQSURA
+        0x0643, // KAF
+        0x0644, // LAM
+        0x0645, // MEEM
+        0x0646, // NOON
+        0x0633, // SEEN
+        0x0639, // AIN
+        0x0641, // FEH
+        0x0635, // SAD
+        // hundreds
+        0x0642, // QAF
+        0x0631, // REH
+        0x0634, // SHEEN
+        0x062A, // TEH
+        0x062B, // THEH
+        0x062E, // KHAH
+        0x0630, // THAL
+        0x0636, // DAD
+        0x0638, // ZAH
+        // thousands
+        0x063A, // GHAIN
+    };
+    private static int[] arabicHijaiAlphabeticMap = {
+        0x0623, // ALEF WITH HAMZA ABOVE
+        0x0628, // BEH
+        0x062A, // TEH
+        0x062B, // THEH
+        0x062C, // JEEM
+        0x062D, // HAH
+        0x062E, // KHAH
+        0x062F, // DAL
+        0x0630, // THAL
+        0x0631, // REH
+        0x0632, // ZAIN
+        0x0633, // SEEN
+        0x0634, // SHEEN
+        0x0635, // SAD
+        0x0636, // DAD
+        0x0637, // TAH
+        0x0638, // ZAH
+        0x0639, // AIN
+        0x063A, // GHAIN
+        0x0641, // FEH
+        0x0642, // QAF
+        0x0643, // KAF
+        0x0644, // LAM
+        0x0645, // MEEM
+        0x0646, // NOON
+        0x0647, // HEH
+        0x0648, // WAW
+        0x0649, // ALEF MAQSURA
+    };
+    private class ArabicNumeralsFormatter implements SpecialNumberFormatter {
+        @Override
+        public Integer[] format ( long number, int one, int letterValue, String features, String language, String country ) {
+            if ( one == 0x0627 ) {
+                int[] map;
+                if ( letterValue == LETTER_VALUE_TRADITIONAL ) {
+                    map = arabicAbjadiAlphabeticMap;
+                } else if ( letterValue == LETTER_VALUE_ALPHABETIC ) {
+                    map = arabicHijaiAlphabeticMap;
+                } else {
+                    map = arabicAbjadiAlphabeticMap;
+                }
+                return formatNumberAsSequence ( number, one, map.length, map );
+            } else if ( one == 0x0623 ) {
+                if ( ( number == 0 ) || ( number > 1999 ) ) {
+                    return null;
+                } else {
+                    return formatAsAbjadiNumber ( number, features, language, country );
+                }
+            } else {
+                return null;
+            }
+        }
+        private Integer[] formatAsAbjadiNumber ( long number, String features, String language, String country ) {
+            List<Integer> sl = new ArrayList<Integer>();
+            assert arabicAbjadiAlphabeticMap.length == 28;
+            assert arabicAbjadiAlphabeticMap[0] == 0x0623;  // ALEF WITH HAMZA ABOVE
+            assert arabicAbjadiAlphabeticMap[27] == 0x063A; // GHAIN
+            assert number != 0;
+            assert number < 2000;
+            int[] map = arabicAbjadiAlphabeticMap;
+            int thousands = (int) ( ( number / 1000 ) % 10 );
+            int hundreds = (int) ( ( number / 100 ) % 10 );
+            int tens = (int) ( ( number / 10 ) % 10 );
+            int ones = (int) ( ( number / 1 ) % 10 );
+            if ( thousands > 0 ) {
+                assert thousands < 2;
+                sl.add ( map [ 27 + ( thousands - 1 ) ] );
+            }
+            if ( hundreds > 0 ) {
+                assert thousands < 10;
+                sl.add ( map [ 18 + ( hundreds - 1 ) ] );
+            }
+            if ( tens > 0 ) {
+                assert tens < 10;
+                sl.add ( map [ 9 + ( tens - 1 ) ] );
+            }
+            if ( ones > 0 ) {
+                assert ones < 10;
+                sl.add ( map [ 0 + ( ones - 1 ) ] );
+            }
+            return sl.toArray ( new Integer [ sl.size() ] );
+        }
+    }
+
+    /**
+     * Kana (Japanese) Numerals
+     */
+    private static int[] hiraganaGojuonAlphabeticMap = {
+        0x3042, // A
+        0x3044, // I
+        0x3046, // U
+        0x3048, // E
+        0x304A, // O
+        0x304B, // KA
+        0x304D, // KI
+        0x304F, // KU
+        0x3051, // KE
+        0x3053, // KO
+        0x3055, // SA
+        0x3057, // SI
+        0x3059, // SU
+        0x305B, // SE
+        0x305D, // SO
+        0x305F, // TA
+        0x3061, // TI
+        0x3064, // TU
+        0x3066, // TE
+        0x3068, // TO
+        0x306A, // NA
+        0x306B, // NI
+        0x306C, // NU
+        0x306D, // NE
+        0x306E, // NO
+        0x306F, // HA
+        0x3072, // HI
+        0x3075, // HU
+        0x3078, // HE
+        0x307B, // HO
+        0x307E, // MA
+        0x307F, // MI
+        0x3080, // MU
+        0x3081, // ME
+        0x3082, // MO
+        0x3084, // YA
+        0x3086, // YU
+        0x3088, // YO
+        0x3089, // RA
+        0x308A, // RI
+        0x308B, // RU
+        0x308C, // RE
+        0x308D, // RO
+        0x308F, // WA
+        0x3090, // WI
+        0x3091, // WE
+        0x3092, // WO
+        0x3093, // N
+    };
+    private static int[] katakanaGojuonAlphabeticMap = {
+        0x30A2, // A
+        0x30A4, // I
+        0x30A6, // U
+        0x30A8, // E
+        0x30AA, // O
+        0x30AB, // KA
+        0x30AD, // KI
+        0x30AF, // KU
+        0x30B1, // KE
+        0x30B3, // KO
+        0x30B5, // SA
+        0x30B7, // SI
+        0x30B9, // SU
+        0x30BB, // SE
+        0x30BD, // SO
+        0x30BF, // TA
+        0x30C1, // TI
+        0x30C4, // TU
+        0x30C6, // TE
+        0x30C8, // TO
+        0x30CA, // NA
+        0x30CB, // NI
+        0x30CC, // NU
+        0x30CD, // NE
+        0x30CE, // NO
+        0x30CF, // HA
+        0x30D2, // HI
+        0x30D5, // HU
+        0x30D8, // HE
+        0x30DB, // HO
+        0x30DE, // MA
+        0x30DF, // MI
+        0x30E0, // MU
+        0x30E1, // ME
+        0x30E2, // MO
+        0x30E4, // YA
+        0x30E6, // YU
+        0x30E8, // YO
+        0x30E9, // RA
+        0x30EA, // RI
+        0x30EB, // RU
+        0x30EC, // RE
+        0x30ED, // RO
+        0x30EF, // WA
+        0x30F0, // WI
+        0x30F1, // WE
+        0x30F2, // WO
+        0x30F3, // N
+    };
+    private class KanaNumeralsFormatter implements SpecialNumberFormatter {
+        @Override
+        public Integer[] format ( long number, int one, int letterValue, String features, String language, String country ) {
+            if ( ( one == 0x3042 ) && ( letterValue == LETTER_VALUE_ALPHABETIC ) ) {
+                return formatNumberAsSequence ( number, one, hiraganaGojuonAlphabeticMap.length, hiraganaGojuonAlphabeticMap );
+            } else if ( ( one == 0x30A2 ) && ( letterValue == LETTER_VALUE_ALPHABETIC ) ) {
+                return formatNumberAsSequence ( number, one, katakanaGojuonAlphabeticMap.length, katakanaGojuonAlphabeticMap );
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Thai Numerals
+     */
+    private static int[] thaiAlphabeticMap = {
+        0x0E01,
+        0x0E02,
+        0x0E03,
+        0x0E04,
+        0x0E05,
+        0x0E06,
+        0x0E07,
+        0x0E08,
+        0x0E09,
+        0x0E0A,
+        0x0E0B,
+        0x0E0C,
+        0x0E0D,
+        0x0E0E,
+        0x0E0F,
+        0x0E10,
+        0x0E11,
+        0x0E12,
+        0x0E13,
+        0x0E14,
+        0x0E15,
+        0x0E16,
+        0x0E17,
+        0x0E18,
+        0x0E19,
+        0x0E1A,
+        0x0E1B,
+        0x0E1C,
+        0x0E1D,
+        0x0E1E,
+        0x0E1F,
+        0x0E20,
+        0x0E21,
+        0x0E22,
+        0x0E23,
+        // 0x0E24, // RU - not used in modern sequence
+        0x0E25,
+        // 0x0E26, // LU - not used in modern sequence
+        0x0E27,
+        0x0E28,
+        0x0E29,
+        0x0E2A,
+        0x0E2B,
+        0x0E2C,
+        0x0E2D,
+        0x0E2E,
+    };
+    private class ThaiNumeralsFormatter implements SpecialNumberFormatter {
+        @Override
+        public Integer[] format ( long number, int one, int letterValue, String features, String language, String country ) {
+            if ( ( one == 0x0E01 ) && ( letterValue == LETTER_VALUE_ALPHABETIC ) ) {
+                return formatNumberAsSequence ( number, one, thaiAlphabeticMap.length, thaiAlphabeticMap );
+            } else {
+                return null;
+            }
+        }
+    }
+
+}

Propchange: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/util/NumberConverter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: xmlgraphics/fop/branches/Temp_ComplexScripts/src/java/org/apache/fop/util/NumberConverter.java
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/src/sandbox/org/apache/fop/render/mif/MIFHandler.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/src/sandbox/org/apache/fop/render/mif/MIFHandler.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/src/sandbox/org/apache/fop/render/mif/MIFHandler.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/src/sandbox/org/apache/fop/render/mif/MIFHandler.java Tue Oct 18 08:33:32 2011
@@ -84,7 +84,8 @@ public class MIFHandler extends FOEventH
     public MIFHandler(FOUserAgent ua, OutputStream os) {
         super(ua);
         outStream = os;
-        FontSetup.setup(fontInfo, null, new DefaultFontResolver(ua));
+        boolean base14Kerning = false; //TODO - FIXME
+        FontSetup.setup(fontInfo, null, new DefaultFontResolver(ua), base14Kerning);
     }
 
     /** {@inheritDoc} */

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/status.xml
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/status.xml?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/status.xml (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/status.xml Tue Oct 18 08:33:32 2011
@@ -60,6 +60,13 @@
       documents. Example: the fix of marks layering will be such a case when it's done.
     -->
     <release version="FOP Trunk" date="TBD">
+      <action context="Code" dev="PH" type="add" fixes-bug="51962" due-to="Mehdi Houshmand">
+        Bugfix for when the last simple-page-master referenced in a page-sequence-master is not
+        chosen when force-page-count=odd.
+      </action>
+      <action context="Code" dev="VH" type="add" fixes-bug="51928" due-to="Mehdi Houshmand">
+        Upgraded all tests to JUnit 4.
+      </action>
       <action context="Fonts" dev="PH" type="fix" fixes-bug="48696">
         Bugfix for color model in IOCA IDE structure parameter for 4- and 8-bit grayscale images.
         Revision 4.

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/AbstractBasicTranscoderTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/AbstractBasicTranscoderTestCase.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/AbstractBasicTranscoderTestCase.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/AbstractBasicTranscoderTestCase.java Tue Oct 18 08:33:32 2011
@@ -19,6 +19,8 @@
 
 package org.apache.fop;
 
+import static org.junit.Assert.assertTrue;
+
 import java.io.File;
 import java.io.InputStream;
 
@@ -26,6 +28,7 @@ import org.apache.batik.transcoder.Trans
 import org.apache.batik.transcoder.TranscoderInput;
 import org.apache.batik.transcoder.TranscoderOutput;
 import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.junit.Test;
 
 /**
  * Basic runtime test for FOP's transcoders. It is used to verify that
@@ -34,13 +37,6 @@ import org.apache.commons.io.output.Byte
 public abstract class AbstractBasicTranscoderTestCase extends AbstractFOPTestCase {
 
     /**
-     * @see junit.framework.TestCase#TestCase(String)
-     */
-    public AbstractBasicTranscoderTestCase(String name) {
-        super(name);
-    }
-
-    /**
      * Creates the transcoder to test.
      * @return the newly instantiated transcoder
      */
@@ -51,6 +47,7 @@ public abstract class AbstractBasicTrans
      * Without special configuration stuff.
      * @throws Exception if a problem occurs
      */
+    @Test
     public void testGenericPDFTranscoder() throws Exception {
         //Create transcoder
         Transcoder transcoder = createTranscoder();

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/AbstractFOPTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/AbstractFOPTestCase.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/AbstractFOPTestCase.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/AbstractFOPTestCase.java Tue Oct 18 08:33:32 2011
@@ -21,25 +21,16 @@ package org.apache.fop;
 
 import java.io.File;
 
-import junit.framework.TestCase;
-
 /**
  * Abstract base test class for FOP's tests.
  */
-public abstract class AbstractFOPTestCase extends TestCase {
-
-    /**
-     * @see junit.framework.TestCase#TestCase(String)
-     */
-    public AbstractFOPTestCase(String name) {
-        super(name);
-    }
+public abstract class AbstractFOPTestCase {
 
     /**
      * Returns the base directory to use for the tests.
      * @return the base directory
      */
-    protected File getBaseDir() {
+    protected static File getBaseDir() {
         String basedir = System.getProperty("basedir");
         if (basedir != null) {
             return new File(basedir);

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicDriverTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicDriverTestCase.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicDriverTestCase.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicDriverTestCase.java Tue Oct 18 08:33:32 2011
@@ -19,6 +19,8 @@
 
 package org.apache.fop;
 
+import static org.junit.Assert.assertTrue;
+
 import java.io.File;
 
 import javax.xml.transform.Result;
@@ -34,6 +36,7 @@ import org.apache.fop.apps.Fop;
 import org.apache.fop.apps.FopFactory;
 import org.apache.fop.apps.MimeConstants;
 import org.apache.fop.cli.InputHandler;
+import org.junit.Test;
 
 /**
  * Basic runtime test for the old Fop class. It is used to verify that
@@ -44,16 +47,10 @@ public class BasicDriverTestCase extends
     private FopFactory fopFactory = FopFactory.newInstance();
 
     /**
-     * @see junit.framework.TestCase#TestCase(String)
-     */
-    public BasicDriverTestCase(String name) {
-        super(name);
-    }
-
-    /**
      * Tests Fop with JAXP and OutputStream generating PDF.
      * @throws Exception if anything fails
      */
+    @Test
     public void testFO2PDFWithJAXP() throws Exception {
         FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
         File foFile = new File(getBaseDir(), "test/xml/bugtests/block.fo");
@@ -73,6 +70,7 @@ public class BasicDriverTestCase extends
      * Tests Fop with JAXP and OutputStream generating PostScript.
      * @throws Exception if anything fails
      */
+    @Test
     public void testFO2PSWithJAXP() throws Exception {
         FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
         File foFile = new File(getBaseDir(), "test/xml/bugtests/block.fo");
@@ -92,6 +90,7 @@ public class BasicDriverTestCase extends
      * Tests Fop with JAXP and OutputStream generating RTF.
      * @throws Exception if anything fails
      */
+    @Test
     public void testFO2RTFWithJAXP() throws Exception {
         FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
         File foFile = new File(getBaseDir(), "test/xml/bugtests/block.fo");
@@ -111,6 +110,7 @@ public class BasicDriverTestCase extends
      * Tests Fop with XsltInputHandler and OutputStream.
      * @throws Exception if anything fails
      */
+    @Test
     public void testFO2PDFWithXSLTInputHandler() throws Exception {
         FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
         File xmlFile = new File(getBaseDir(), "test/xml/1.xml");

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicDriverTestSuite.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicDriverTestSuite.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicDriverTestSuite.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicDriverTestSuite.java Tue Oct 18 08:33:32 2011
@@ -19,24 +19,14 @@
 
 package org.apache.fop;
 
-import junit.framework.Test;
-import junit.framework.TestSuite;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
 
 /**
  * Test suite for basic functionality of FOP's Driver API.
  */
+@RunWith(Suite.class)
+@SuiteClasses({ BasicDriverTestCase.class })
 public class BasicDriverTestSuite {
-
-    /**
-     * Builds the test suite
-     * @return the test suite
-     */
-    public static Test suite() {
-        TestSuite suite = new TestSuite(
-            "Basic functionality test suite for FOP's Driver API");
-        //$JUnit-BEGIN$
-        suite.addTest(new TestSuite(BasicDriverTestCase.class));
-        //$JUnit-END$
-        return suite;
-    }
 }

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicPDFTranscoderTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicPDFTranscoderTestCase.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicPDFTranscoderTestCase.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicPDFTranscoderTestCase.java Tue Oct 18 08:33:32 2011
@@ -28,16 +28,7 @@ import org.apache.fop.svg.PDFTranscoder;
  */
 public class BasicPDFTranscoderTestCase extends AbstractBasicTranscoderTestCase {
 
-    /**
-     * @see junit.framework.TestCase#TestCase(String)
-     */
-    public BasicPDFTranscoderTestCase(String name) {
-        super(name);
-    }
-
-    /**
-     * @see org.apache.fop.AbstractBasicTranscoderTestCase#createTranscoder()
-     */
+    @Override
     protected Transcoder createTranscoder() {
         return new PDFTranscoder();
     }

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicPSTranscoderTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicPSTranscoderTestCase.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicPSTranscoderTestCase.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicPSTranscoderTestCase.java Tue Oct 18 08:33:32 2011
@@ -28,16 +28,7 @@ import org.apache.fop.render.ps.PSTransc
  */
 public class BasicPSTranscoderTestCase extends AbstractBasicTranscoderTestCase {
 
-    /**
-     * @see junit.framework.TestCase#TestCase(String)
-     */
-    public BasicPSTranscoderTestCase(String name) {
-        super(name);
-    }
-
-    /**
-     * @see org.apache.fop.AbstractBasicTranscoderTestCase#createTranscoder()
-     */
+    @Override
     protected Transcoder createTranscoder() {
         return new PSTranscoder();
     }

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicTranscoderTestSuite.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicTranscoderTestSuite.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicTranscoderTestSuite.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/BasicTranscoderTestSuite.java Tue Oct 18 08:33:32 2011
@@ -19,25 +19,17 @@
 
 package org.apache.fop;
 
-import junit.framework.Test;
-import junit.framework.TestSuite;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
 
 /**
  * Test suite for basic functionality of FOP's transcoders.
  */
+@RunWith(Suite.class)
+@SuiteClasses({
+    BasicPDFTranscoderTestCase.class,
+    BasicPSTranscoderTestCase.class
+})
 public class BasicTranscoderTestSuite {
-
-    /**
-     * Builds the test suite
-     * @return the test suite
-     */
-    public static Test suite() {
-        TestSuite suite = new TestSuite(
-            "Basic functionality test suite for FOP's transcoders");
-        //$JUnit-BEGIN$
-        suite.addTest(new TestSuite(BasicPDFTranscoderTestCase.class));
-        suite.addTest(new TestSuite(BasicPSTranscoderTestCase.class));
-        //$JUnit-END$
-        return suite;
-    }
 }

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/DigestFilterTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/DigestFilterTestCase.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/DigestFilterTestCase.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/DigestFilterTestCase.java Tue Oct 18 08:33:32 2011
@@ -19,6 +19,8 @@
 
 package org.apache.fop;
 
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.io.StringReader;
 import java.security.NoSuchAlgorithmException;
@@ -26,9 +28,9 @@ import java.security.NoSuchAlgorithmExce
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParserFactory;
 
-import junit.framework.TestCase;
-
 import org.apache.fop.util.DigestFilter;
+import org.junit.Before;
+import org.junit.Test;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
@@ -37,12 +39,12 @@ import org.xml.sax.XMLReader;
  * Test case for digesting SAX filter.
  *
  */
-public class DigestFilterTestCase extends TestCase {
+public class DigestFilterTestCase {
 
     private SAXParserFactory parserFactory;
 
-    /** @see junit.framework.TestCase#setUp() */
-    protected void setUp() {
+    @Before
+    public void setUp() {
         parserFactory = SAXParserFactory.newInstance();
         parserFactory.setNamespaceAware(true);
     }
@@ -95,6 +97,7 @@ public class DigestFilterTestCase extend
         return digestFilter.getDigestValue();
     }
 
+    @Test
     public final void testLineFeed()
         throws
             NoSuchAlgorithmException,
@@ -111,6 +114,7 @@ public class DigestFilterTestCase extend
             compareDigest(lfDigest, crlfDigest));
     }
 
+    @Test
     public final void testAttributeOrder()
         throws
             NoSuchAlgorithmException,
@@ -134,6 +138,7 @@ public class DigestFilterTestCase extend
             compareDigest(sortDigest, reverseDigest));
     }
 
+    @Test
     public final void testNamespacePrefix()
         throws
             NoSuchAlgorithmException,

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/GenericFOPTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/GenericFOPTestCase.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/GenericFOPTestCase.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/GenericFOPTestCase.java Tue Oct 18 08:33:32 2011
@@ -18,6 +18,7 @@
 /* $Id$ */
 
 package org.apache.fop;
+import static org.junit.Assert.fail;
 
 import java.io.ByteArrayOutputStream;
 import java.io.StringReader;
@@ -27,10 +28,8 @@ import java.util.Date;
 
 import javax.xml.parsers.SAXParserFactory;
 
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
+import org.junit.Before;
+import org.junit.Test;
 import org.xml.sax.InputSource;
 
 import org.apache.fop.apps.FOUserAgent;
@@ -47,33 +46,20 @@ import org.apache.fop.util.DigestFilter;
  * PDF currently supported) and a MD5 for the result.
  *
  */
-public final class GenericFOPTestCase extends TestCase {
+public final class GenericFOPTestCase {
 
     // configure fopFactory as desired
     private FopFactory fopFactory = FopFactory.newInstance();
 
-    protected SAXParserFactory parserFactory;
-
-    public static Test suite() {
-        TestSuite suite = new TestSuite(GenericFOPTestCase.class);
-        suite.setName("Fop regression tests");
-        return suite;
-    }
-
-    /**
-     * Constructor for FopTest.
-     * @param name the name of the test suite
-     */
-    public GenericFOPTestCase(String name) {
-        super(name);
-    }
+    private SAXParserFactory parserFactory;
 
-    /** @see junit.framework.TestCase#setUp() */
-    protected void setUp() throws Exception {
+    @Before
+    public void setUp() throws Exception {
         parserFactory = SAXParserFactory.newInstance();
         parserFactory.setNamespaceAware(true);
     }
 
+    @org.junit.Test
     public void testSimple() throws Exception {
         final String digestIn = "17bf13298796065f7775db8707133aeb";
         final String digestOut = "e2761f51152f6663911e567901596707";
@@ -119,8 +105,7 @@ public final class GenericFOPTestCase ex
         FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
         foUserAgent.setCreationDate(new Date(10000));
         MessageDigest outDigest = MessageDigest.getInstance("MD5");
-        DigestOutputStream out
-            = new DigestOutputStream(new ByteArrayOutputStream(), outDigest);
+        DigestOutputStream out = new DigestOutputStream(new ByteArrayOutputStream(), outDigest);
         Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
         InputSource source = new InputSource(new StringReader(fo));
         DigestFilter filter = new DigestFilter("MD5");

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/KnuthAlgorithmTestCase.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/KnuthAlgorithmTestCase.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/KnuthAlgorithmTestCase.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/KnuthAlgorithmTestCase.java Tue Oct 18 08:33:32 2011
@@ -19,6 +19,8 @@
 
 package org.apache.fop;
 
+import static org.junit.Assert.assertEquals;
+
 import java.util.List;
 
 import org.apache.fop.layoutmgr.BlockKnuthSequence;
@@ -28,17 +30,16 @@ import org.apache.fop.layoutmgr.KnuthBox
 import org.apache.fop.layoutmgr.KnuthGlue;
 import org.apache.fop.layoutmgr.KnuthPenalty;
 import org.apache.fop.layoutmgr.KnuthSequence;
-
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
 
 /**
  * Tests the Knuth algorithm implementation.
  */
-public class KnuthAlgorithmTestCase extends TestCase {
+public class KnuthAlgorithmTestCase {
 
-    /** @see junit.framework.TestCase#setUp() */
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() {
         DebugHelper.registerStandardElementListObservers();
     }
 
@@ -67,6 +68,7 @@ public class KnuthAlgorithmTestCase exte
      * possibility.
      * @throws Exception if an error occurs
      */
+    @Test
     public void test1() throws Exception {
         MyBreakingAlgorithm algo = new MyBreakingAlgorithm(0, 0, true, true, 0);
         algo.setConstantLineWidth(30000);

Modified: xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/StandardTestSuite.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/StandardTestSuite.java?rev=1185530&r1=1185529&r2=1185530&view=diff
==============================================================================
--- xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/StandardTestSuite.java (original)
+++ xmlgraphics/fop/branches/Temp_ComplexScripts/test/java/org/apache/fop/StandardTestSuite.java Tue Oct 18 08:33:32 2011
@@ -19,8 +19,9 @@
 
 package org.apache.fop;
 
-import junit.framework.Test;
-import junit.framework.TestSuite;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
 
 import org.apache.fop.afp.fonts.CharactersetEncoderTest;
 import org.apache.fop.afp.parser.MODCAParserTestCase;
@@ -36,6 +37,7 @@ import org.apache.fop.render.pdf.PDFACon
 import org.apache.fop.render.pdf.PDFCMapTestCase;
 import org.apache.fop.render.pdf.PDFEncodingTestCase;
 import org.apache.fop.render.pdf.PDFsRGBSettingsTestCase;
+import org.apache.fop.render.pdf.RenderPDFTestSuite;
 import org.apache.fop.render.ps.PSTestSuite;
 import org.apache.fop.render.rtf.RichTextFormatTestSuite;
 import org.apache.fop.traits.MinOptMaxTest;
@@ -43,37 +45,30 @@ import org.apache.fop.traits.MinOptMaxTe
 /**
  * Test suite for basic functionality of FOP.
  */
+@RunWith(Suite.class)
+@SuiteClasses({
+    BasicDriverTestSuite.class,
+    UtilityCodeTestSuite.class,
+    PDFAConformanceTestCase.class,
+    PDFEncodingTestCase.class,
+    PDFCMapTestCase.class,
+    PDFsRGBSettingsTestCase.class,
+    DejaVuLGCSerifTest.class,
+    RichTextFormatTestSuite.class,
+    ImageLoaderTestCase.class,
+    ImagePreloaderTestCase.class,
+    IFMimickingTestCase.class,
+    PageBoundariesTest.class,
+    PageScaleTest.class,
+    org.apache.fop.afp.AFPTestSuite.class,
+    GlyfTableTestCase.class,
+    ViewportTestSuite.class,
+    RenderPDFTestSuite.class,
+    MODCAParserTestCase.class,
+    CharactersetEncoderTest.class,
+    org.apache.fop.render.afp.AFPTestSuite.class,
+    PSTestSuite.class,
+    MinOptMaxTest.class
+})
 public class StandardTestSuite {
-
-    /**
-     * Builds the test suite
-     * @return the test suite
-     */
-    public static Test suite() {
-        TestSuite suite = new TestSuite("Basic functionality test suite for FOP");
-        //$JUnit-BEGIN$
-        suite.addTest(BasicDriverTestSuite.suite());
-        suite.addTest(UtilityCodeTestSuite.suite());
-        suite.addTest(org.apache.fop.afp.AFPTestSuite.suite());
-        suite.addTest(new TestSuite(PDFAConformanceTestCase.class));
-        suite.addTest(new TestSuite(PDFEncodingTestCase.class));
-        suite.addTest(new TestSuite(PDFCMapTestCase.class));
-        suite.addTest(new TestSuite(PDFsRGBSettingsTestCase.class));
-        suite.addTest(new TestSuite(DejaVuLGCSerifTest.class));
-        suite.addTest(new TestSuite(MODCAParserTestCase.class));
-        suite.addTest(new TestSuite(CharactersetEncoderTest.class));
-        suite.addTest(org.apache.fop.render.afp.AFPTestSuite.suite());
-        suite.addTest(PSTestSuite.suite());
-        suite.addTest(new TestSuite(GlyfTableTestCase.class));
-        suite.addTest(RichTextFormatTestSuite.suite());
-        suite.addTest(new TestSuite(ImageLoaderTestCase.class));
-        suite.addTest(new TestSuite(ImagePreloaderTestCase.class));
-        suite.addTest(new TestSuite(IFMimickingTestCase.class));
-        suite.addTest(new TestSuite(PageBoundariesTest.class));
-        suite.addTest(new TestSuite(PageScaleTest.class));
-        suite.addTest(new TestSuite(MinOptMaxTest.class));
-        suite.addTest(ViewportTestSuite.suite());
-        //$JUnit-END$
-        return suite;
-    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org


Mime
View raw message