Skip to content

Commit

Permalink
MONDRIAN: Limited support for formatting string values.
Browse files Browse the repository at this point in the history
[git-p4: depot-paths = "//open/mondrian-release/3.2/": change = 13401]
  • Loading branch information
julianhyde committed Feb 21, 2010
1 parent 25a4cba commit 48191cd
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 50 deletions.
109 changes: 65 additions & 44 deletions src/main/mondrian/util/Format.java
Expand Up @@ -51,7 +51,7 @@
*
* <p>Still to be implemented:<ul>
*
* <li>String formatting (upper-case, lower-case, fill from left/right)</li>
* <li>String formatting (fill from left/right)</li>
*
* <li>Use client's timezone for printing times.</li>
*
Expand All @@ -64,7 +64,6 @@ public class Format {
private String formatString;
private BasicFormat format;
private FormatLocale locale;
private static final FieldPosition dummyFieldPos = createDummyFieldPos();

/**
* Maximum number of entries in the format cache used by
Expand All @@ -88,17 +87,13 @@ private static FieldPosition createDummyFieldPos() {
*
* <p>If the number of entries in the cache exceeds 1000,
*/
private static Map<String, Format> cache =
private static final Map<String, Format> cache =
new LinkedHashMap<String, Format>() {
public boolean removeEldestEntry(Map.Entry<String, Format> entry) {
return size() > CacheLimit;
}
};

static final char[] digits = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};

static final char thousandSeparator_en = ',';
static final char decimalPlaceholder_en = '.';
static final String dateSeparator_en = "/";
Expand Down Expand Up @@ -143,6 +138,8 @@ public boolean removeEldestEntry(Map.Entry<String, Format> entry) {
* <p>If you need to format many objects using the same format string,
* create a formatter object using
* {@link mondrian.util.Format#Format(String, java.util.Locale)}.
*
* @return object formatted using format string
*/
static String format(Object o, String formatString, Locale locale)
{
Expand Down Expand Up @@ -220,18 +217,6 @@ static class BasicFormat {
this.code = code;
}

boolean isNumeric() {
return false;
}

boolean isDate() {
return false;
}

boolean isString() {
return false;
}

void formatNull(StringBuilder buf) {
// SSAS formats null values as the empty string. However, SQL Server
// Management Studio's pivot table formats them as "(null)", so many
Expand Down Expand Up @@ -454,7 +439,7 @@ void format(long n, StringBuilder buf) {
buf.append(s);
}

void format(String s, StringBuilder buf) {
void format(String str, StringBuilder buf) {
buf.append(s);
}

Expand Down Expand Up @@ -678,7 +663,6 @@ void format(double n, StringBuilder buf)
if (n == 0.0 || (n < 0 && !shows(fd, formatDigitsRightOfPoint))) {
// Underflow of negative number. Make it zero, so there is no
// '-' sign.
n = 0;
fd = new FloatingDecimal(0);
}
String s = fd.toJavaFormatString(
Expand Down Expand Up @@ -1092,17 +1076,49 @@ private FormatLocale(

private static class StringFormat extends BasicFormat
{
int stringCase;
final StringCase stringCase;
final String literal;

StringFormat(int stringCase) {
StringFormat(StringCase stringCase, String literal) {
assert stringCase != null;
this.stringCase = stringCase;
this.literal = literal;
}

@Override
void format(String s, StringBuilder buf) {
switch (stringCase) {
case UPPER:
s = s.toUpperCase();
break;
case LOWER:
s = s.toLowerCase();
break;
}
buf.append(s);
}

void format(double d, StringBuilder buf) {
buf.append(literal);
}

void format(long n, StringBuilder buf) {
buf.append(literal);
}

void format(Date date, StringBuilder buf) {
buf.append(literal);
}

void format(Calendar calendar, StringBuilder buf) {
buf.append(literal);
}
}

/** Values for {@link StringFormat#stringCase}. */
private static final int CASE_ASIS = 0;
private static final int CASE_UPPER = 1;
private static final int CASE_LOWER = 2;
private enum StringCase {
UPPER,
LOWER
}

/** Types of Format. */
private static final int GENERAL = 0;
Expand Down Expand Up @@ -1172,20 +1188,20 @@ private static class StringFormat extends BasicFormat
private static final int FORMAT_MMMM_LOWER = 56;
private static final int FORMAT_USD = 57;

private static final Token nfe(
private static Token nfe(
int code, int flags, String token, String purpose, String description)
{
Util.discard(purpose);
Util.discard(description);
return new Token(code, flags, token);
}

public static final List<Token> getTokenList()
public static List<Token> getTokenList()
{
return Collections.unmodifiableList(Arrays.asList(tokens));
}

static final Token[] tokens = {
private static final Token[] tokens = {
nfe(
FORMAT_NULL,
NUMERIC,
Expand Down Expand Up @@ -1763,6 +1779,11 @@ public Format(String formatString, Locale locale)
/**
* Constructs a <code>Format</code> in a specific locale.
*
* @param formatString the format string; see
* <a href="http://www.apostate.com/programming/vb-format.html">this
* description</a> for more details
* @param locale The locale
*
* @see FormatLocale
* @see #createLocale
*/
Expand Down Expand Up @@ -1808,13 +1829,15 @@ public Format(String formatString, FormatLocale locale)
* @param formatString the format string; see
* <a href="http://www.apostate.com/programming/vb-format.html">this
* description</a> for more details
*
* @return format for given format string in given locale
*/
public static Format get(String formatString, Locale locale) {
String key = formatString + "@@@" + locale;
Format format = (Format) cache.get(key);
Format format = cache.get(key);
if (format == null) {
synchronized (cache) {
format = (Format) cache.get(key);
format = cache.get(key);
if (format == null) {
format = new Format(formatString, locale);
cache.put(key, format);
Expand Down Expand Up @@ -2026,8 +2049,7 @@ public static FormatLocale registerFormatLocale(
FormatLocale formatLocale, Locale locale)
{
String key = locale.toString(); // e.g. "en_us_Boston"
FormatLocale previous = mapLocaleToFormatLocale.put(key, formatLocale);
return previous;
return mapLocaleToFormatLocale.put(key, formatLocale);
}

// Values for variable numberState below.
Expand Down Expand Up @@ -2055,7 +2077,6 @@ private String parseFormatString(
zeroesLeftOfPoint = 0,
zeroesRightOfPoint = 0,
zeroesRightOfExp = 0;
int stringCase = CASE_ASIS;
boolean useDecimal = false,
useThouSep = false,
fillFromRight = true;
Expand Down Expand Up @@ -2224,7 +2245,7 @@ private String parseFormatString(
case FORMAT_BACKSLASH:
{
// Display the next character in the format string.
String s = "";
String s;
if (formatString.length() == 1) {
// Backslash is the last character in the
// string.
Expand Down Expand Up @@ -2278,13 +2299,13 @@ private String parseFormatString(

case FORMAT_UPPER:
{
stringCase = CASE_UPPER;
format = new StringFormat(StringCase.UPPER, ">");
break;
}

case FORMAT_LOWER:
{
stringCase = CASE_LOWER;
format = new StringFormat(StringCase.LOWER, "<");
break;
}

Expand Down Expand Up @@ -2490,17 +2511,17 @@ private StringBuilder format(Object o, StringBuilder buf) {
// class equality than using 'instanceof'.
Class<? extends Object> clazz = o.getClass();
if (clazz == Double.class) {
format.format(((Double) o).doubleValue(), buf);
format.format((Double) o, buf);
} else if (clazz == Float.class) {
format.format(((Float) o).floatValue(), buf);
format.format((Float) o, buf);
} else if (clazz == Integer.class) {
format.format(((Integer) o).intValue(), buf);
format.format((Integer) o, buf);
} else if (clazz == Long.class) {
format.format(((Long) o).longValue(), buf);
format.format((Long) o, buf);
} else if (clazz == Short.class) {
format.format(((Short) o).shortValue(), buf);
format.format((Short) o, buf);
} else if (clazz == Byte.class) {
format.format(((Byte) o).byteValue(), buf);
format.format((Byte) o, buf);
} else if (o instanceof BigDecimal) {
format.format(((BigDecimal) o).doubleValue(), buf);
} else if (o instanceof BigInteger) {
Expand Down
52 changes: 46 additions & 6 deletions testsrc/main/mondrian/util/FormatTest.java
Expand Up @@ -3,7 +3,7 @@
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2006-2009 Julian Hyde
// Copyright (C) 2006-2010 Julian Hyde
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/
Expand Down Expand Up @@ -159,9 +159,9 @@ private void checkNumbers(
checkNumber(locale, format, new BigDecimal("0"), result0);
checkNumber(locale, format, new BigDecimal(".6"), resultPoint6);
checkNumber(locale, format, null, resultEmpty);
checkNumber(locale, format, Long.valueOf(6), result6);
checkNumber(locale, format, Long.valueOf(-6), resultNeg6);
checkNumber(locale, format, Long.valueOf(0), result0);
checkNumber(locale, format, 6L, result6);
checkNumber(locale, format, -6L, resultNeg6);
checkNumber(locale, format, 0L, result0);
}

private void checkNumber(
Expand Down Expand Up @@ -390,8 +390,7 @@ private void checkDate(String format, String en, String fr, String de) {
}

public void testAllTokens() {
for (int i = 0; i < Format.tokens.length; i++) {
Format.Token fe = Format.tokens[i];
for (Format.Token fe : Format.getTokenList()) {
Object o;
if (fe.isNumeric()) {
o = d;
Expand Down Expand Up @@ -518,6 +517,47 @@ public void testCache() {
buf.append("#");
}
}

public void testString() {
checkFormat(null, "This Is A Test", ">", "THIS IS A TEST");
checkFormat(null, "This Is A Test", "<", "this is a test");
checkFormat(null, "hello", "\\f\\i\\x\\e\\d", "fixed");
checkFormat(null, "hello", ">\\f\\i\\x\\e\\d<", "HELLOfixedhello");

final BigDecimal decimal = new BigDecimal("123.45");
final int integer = 123;
final String string = "Foo Bar";

// ">"
checkFormat(null, decimal, ">", ">");
checkFormat(null, integer, ">", ">");
checkFormat(null, string, ">", "FOO BAR"); // SSAS 2005 returns ">"

// "<"
checkFormat(null, decimal, "<", "<");
checkFormat(null, integer, "<", "<");
checkFormat(null, string, "<", "foo bar"); // SSAS 2005 returns "<"

// "@" (can't figure out how to use this -- SSAS 2005 always ignores)
checkFormat(null, decimal, "@", "@"); // checked on SSAS 2005
checkFormat(null, integer, "@", "@"); // checked on SSAS 2005
checkFormat(null, string, "@", "@"); // checked on SSAS 2005

// combinations
checkFormat(null, string, "<@", "foo bar@"); // SSAS 2005 returns "<@"
checkFormat(
null, string, "<>", "foo barFOO BAR"); // SSAS 2005 returns "<>"
checkFormat(null, string, "E", "E"); // checked on SSAS 2005

checkFormat(
null, decimal, "E",
"E"); // FIXME: SSAS 2005 returns "1.234500E+002"

checkFormat(null, decimal, "<E", "<E"); // checked on SSAS 2005

// spec and SSAS 2005 disagree
checkFormat(null, string, "\"fixed\"", "fixed");
}
}

// End FormatTest.java

0 comments on commit 48191cd

Please sign in to comment.