net.sf.saxon.expr.number.NumberFormatter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of saxon-he Show documentation
Show all versions of saxon-he Show documentation
An OSGi bundle for Saxon-HE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Saxonica Limited.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.expr.number;
import net.sf.saxon.lib.Numberer;
import net.sf.saxon.regex.Categories;
import net.sf.saxon.regex.UnicodeString;
import net.sf.saxon.tree.util.FastStringBuffer;
import net.sf.saxon.z.IntPredicate;
import net.sf.saxon.z.IntUnionPredicate;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* Class NumberFormatter defines a method to format a ArrayList of integers as a character
* string according to a supplied format specification.
* @author Michael H. Kay
*/
public class NumberFormatter implements Serializable {
private ArrayList formatTokens;
private ArrayList punctuationTokens;
private boolean startsWithPunctuation;
/**
* Tokenize the format pattern.
* @param format the format specification. Contains one of the following values:
* - "1": conventional decimal numbering
* - "a": sequence a, b, c, ... aa, ab, ac, ...
* - "A": sequence A, B, C, ... AA, AB, AC, ...
* - "i": sequence i, ii, iii, iv, v ...
* - "I": sequence I, II, III, IV, V, ...
*
* This symbol may be preceded and followed by punctuation (any other characters) which is
* copied to the output string.
*/
public void prepare(String format) {
// Tokenize the format string into alternating alphanumeric and non-alphanumeric tokens
if (format.length()==0) {
format="1";
}
formatTokens = new ArrayList(10);
punctuationTokens = new ArrayList(10);
UnicodeString uFormat = UnicodeString.makeUnicodeString(format);
int len = uFormat.length();
int i=0;
int t;
boolean first = true;
startsWithPunctuation = true;
while (it) {
UnicodeString tok = uFormat.substring(t, i);
formatTokens.add(tok);
if (first) {
punctuationTokens.add(UnicodeString.makeUnicodeString("."));
startsWithPunctuation = false;
first = false;
}
}
if (i==len) break;
t=i;
c = uFormat.charAt(i);
while (!isLetterOrDigit(c)) {
first = false;
i++;
if (i==len) break;
c = uFormat.charAt(i);
}
if (i>t) {
UnicodeString sep = uFormat.substring(t, i);
punctuationTokens.add(sep);
}
}
if (formatTokens.isEmpty()) {
formatTokens.add(UnicodeString.makeUnicodeString("1"));
if (punctuationTokens.size() == 1) {
punctuationTokens.add(punctuationTokens.get(0));
}
}
}
/**
* Determine whether a (possibly non-BMP) character is a letter or digit.
* @param c the codepoint of the character to be tested
* @return true if this is a number or letter as defined in the XSLT rules for xsl:number pictures.
*/
public static boolean isLetterOrDigit(int c) {
if (c <= 0x7F) {
// Fast path for ASCII characters
return (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A);
} else {
return alphanumeric.matches(c);
}
}
private static IntPredicate alphanumeric =
new IntUnionPredicate(Categories.getCategory("N"), Categories.getCategory("L"));
/**
* Format a list of numbers.
* @param numbers the numbers to be formatted (a sequence of integer values; it may also contain
* preformatted strings as part of the error recovery fallback)
* @return the formatted output string.
*/
public CharSequence format(List numbers, int groupSize, String groupSeparator,
String letterValue, String ordinal, /*@NotNull*/ Numberer numberer) {
FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
int num = 0;
int tok = 0;
// output first punctuation token
if (startsWithPunctuation) {
sb.append(punctuationTokens.get(tok));
}
// output the list of numbers
while (num0) {
if (tok==0 && startsWithPunctuation) {
// The first punctuation token isn't a separator if it appears before the first
// formatting token. Such a punctuation token is used only once, at the start.
sb.append(".");
} else {
sb.append(punctuationTokens.get(tok));
}
}
Object o = numbers.get(num++);
String s;
if (o instanceof Long) {
long nr = (Long) o;
RegularGroupFormatter rgf = new RegularGroupFormatter(groupSize, groupSeparator, UnicodeString.EMPTY_STRING);
s = numberer.format(nr, formatTokens.get(tok), rgf, letterValue, ordinal);
} else {
s = o.toString();
}
sb.append(s);
tok++;
if (tok==formatTokens.size()) tok--;
}
// output the final punctuation token
if (punctuationTokens.size()>formatTokens.size()) {
sb.append(punctuationTokens.get(punctuationTokens.size()-1));
}
return sb.condense();
}
}