diff options
Diffstat (limited to 'libjava/java/text/MessageFormat.java')
-rw-r--r-- | libjava/java/text/MessageFormat.java | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/libjava/java/text/MessageFormat.java b/libjava/java/text/MessageFormat.java new file mode 100644 index 00000000000..8b422357966 --- /dev/null +++ b/libjava/java/text/MessageFormat.java @@ -0,0 +1,543 @@ +// MessageFormat.java - Localized message formatting. + +/* Copyright (C) 1999 Cygnus Solutions + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package java.text; + +import java.util.Date; +import java.util.Locale; +import java.util.Vector; + +/** + * @author Tom Tromey <tromey@cygnus.com> + * @date March 3, 1999 + */ +/* Written using "Java Class Libraries", 2nd edition, plus online + * API docs for JDK 1.2 from http://www.javasoft.com. + * Status: Believed complete and correct to 1.2, except serialization. + * and parsing. + */ + +final class MessageFormatElement +{ + // Argument number. + int argNumber; + // Formatter to be used. This is the format set by setFormat. + Format setFormat; + // Formatter to be used based on the type. + Format format; + + // Argument will be checked to make sure it is an instance of this + // class. + Class formatClass; + + // Formatter type. + String type; + // Formatter style. + String style; + + // Text to follow this element. + String trailer; + + // FIXME: shouldn't need this. + Class forName (String name) + { + try + { + return Class.forName (name); + } + catch (ClassNotFoundException x) + { + } + return null; + } + + // Recompute the locale-based formatter. + void setLocale (Locale loc) + { + if (type == null) + ; + else if (type.equals("number")) + { + // FIXME: named class literal. + // formatClass = Number.class; + formatClass = forName ("java.lang.Number"); + + if (style == null) + format = NumberFormat.getInstance(loc); + else if (style.equals("currency")) + format = NumberFormat.getCurrencyInstance(loc); + else if (style.equals("percent")) + format = NumberFormat.getPercentInstance(loc); + else if (style.equals("integer")) + { + NumberFormat nf = NumberFormat.getNumberInstance(loc); + nf.setMaximumFractionDigits(0); + nf.setGroupingUsed(false); + format = nf; + } + else + { + format = NumberFormat.getNumberInstance(loc); + DecimalFormat df = (DecimalFormat) format; + try + { + df.applyPattern(style); + } + catch (ParseException x) + { + throw new IllegalArgumentException (x.getMessage()); + } + } + } + else if (type.equals("time") || type.equals("date")) + { + // FIXME: named class literal. + // formatClass = Date.class; + formatClass = forName ("java.util.Date"); + + int val = DateFormat.DEFAULT; + if (style == null) + ; + if (style.equals("short")) + val = DateFormat.SHORT; + else if (style.equals("medium")) + val = DateFormat.MEDIUM; + else if (style.equals("long")) + val = DateFormat.LONG; + else if (style.equals("full")) + val = DateFormat.FULL; + + if (type.equals("time")) + format = DateFormat.getTimeInstance(val, loc); + else + format = DateFormat.getDateInstance(val, loc); + + if (style != null && val == DateFormat.DEFAULT) + { + SimpleDateFormat sdf = (SimpleDateFormat) format; + sdf.applyPattern(style); + } + } + else if (type.equals("choice")) + { + // FIXME: named class literal. + // formatClass = Number.class; + formatClass = forName ("java.lang.Number"); + + if (style == null) + throw new + IllegalArgumentException ("style required for choice format"); + format = new ChoiceFormat (style); + } + } +} + +public class MessageFormat extends Format +{ + // Helper that returns the text up to the next format opener. The + // text is put into BUFFER. Returns index of character after end of + // string. Throws IllegalArgumentException on error. + private static final int scanString (String pat, int index, + StringBuffer buffer) + { + int max = pat.length(); + buffer.setLength(0); + for (; index < max; ++index) + { + char c = pat.charAt(index); + if (c == '\'' && index + 2 < max && pat.charAt(index + 2) == '\'') + { + buffer.append(pat.charAt(index + 1)); + index += 2; + } + else if (c == '\'' && index + 1 < max + && pat.charAt(index + 1) == '\'') + { + buffer.append(c); + ++index; + } + else if (c == '{') + break; + else if (c == '}') + throw new IllegalArgumentException (); + else + buffer.append(c); + } + return index; + } + + // This helper retrieves a single part of a format element. Returns + // the index of the terminating character. + private static final int scanFormatElement (String pat, int index, + StringBuffer buffer, + char term) + { + int max = pat.length(); + buffer.setLength(0); + int brace_depth = 1; + + for (; index < max; ++index) + { + char c = pat.charAt(index); + if (c == '\'' && index + 2 < max && pat.charAt(index + 2) == '\'') + { + buffer.append(c); + buffer.append(pat.charAt(index + 1)); + buffer.append(c); + index += 2; + } + else if (c == '\'' && index + 1 < max + && pat.charAt(index + 1) == '\'') + { + buffer.append(c); + ++index; + } + else if (c == '{') + { + buffer.append(c); + ++brace_depth; + } + else if (c == '}') + { + if (--brace_depth == 0) + break; + buffer.append(c); + } + // Check for TERM after braces, because TERM might be `}'. + else if (c == term) + break; + else + buffer.append(c); + } + return index; + } + + // This is used to parse a format element and whatever non-format + // text might trail it. + private static final int scanFormat (String pat, int index, + StringBuffer buffer, Vector elts, + Locale locale) + { + MessageFormatElement mfe = new MessageFormatElement (); + elts.addElement(mfe); + + int max = pat.length(); + + // Skip the opening `{'. + ++index; + + // Fetch the argument number. + index = scanFormatElement (pat, index, buffer, ','); + try + { + mfe.argNumber = Integer.parseInt(buffer.toString()); + } + catch (NumberFormatException nfx) + { + throw new IllegalArgumentException (); + } + + // Extract the element format. + if (index < max && pat.charAt(index) == ',') + { + index = scanFormatElement (pat, index + 1, buffer, ','); + mfe.type = buffer.toString(); + + // Extract the style. + if (index < max && pat.charAt(index) == ',') + { + index = scanFormatElement (pat, index + 1, buffer, '}'); + mfe.style = buffer.toString (); + } + } + + // Advance past the last terminator. + if (index >= max || pat.charAt(index) != '}') + throw new IllegalArgumentException (); + ++index; + + // Now fetch trailing string. + index = scanString (pat, index, buffer); + mfe.trailer = buffer.toString (); + + mfe.setLocale(locale); + + return index; + } + + public void applyPattern (String newPattern) + { + pattern = newPattern; + + StringBuffer tempBuffer = new StringBuffer (); + + int index = scanString (newPattern, 0, tempBuffer); + leader = tempBuffer.toString(); + + Vector elts = new Vector (); + while (index < newPattern.length()) + index = scanFormat (newPattern, index, tempBuffer, elts, locale); + + elements = new MessageFormatElement[elts.size()]; + elts.copyInto(elements); + } + + public Object clone () + { + MessageFormat c = new MessageFormat (); + c.setLocale(locale); + c.applyPattern(pattern); + return (Object) c; + } + + public boolean equals (Object obj) + { + if (! (obj instanceof MessageFormat)) + return false; + MessageFormat mf = (MessageFormat) obj; + return (pattern.equals(mf.pattern) + && locale.equals(mf.locale)); + } + + public static String format (String pattern, Object arguments[]) + { + MessageFormat mf = new MessageFormat (pattern); + StringBuffer sb = new StringBuffer (); + FieldPosition fp = new FieldPosition (NumberFormat.INTEGER_FIELD); + return mf.format(arguments, sb, fp).toString(); + } + + public final StringBuffer format (Object arguments[], StringBuffer appendBuf, + FieldPosition ignore) + { + appendBuf.append(leader); + + for (int i = 0; i < elements.length; ++i) + { + if (elements[i].argNumber >= arguments.length) + throw new IllegalArgumentException (); + Object thisArg = arguments[elements[i].argNumber]; + + Format formatter = null; + if (elements[i].setFormat != null) + formatter = elements[i].setFormat; + else if (elements[i].format != null) + { + if (elements[i].formatClass != null + && ! elements[i].formatClass.isInstance(thisArg)) + throw new IllegalArgumentException (); + formatter = elements[i].format; + } + else if (thisArg instanceof Number) + formatter = NumberFormat.getInstance(locale); + else if (thisArg instanceof Date) + formatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale); + else + appendBuf.append(thisArg); + + if (formatter != null) + { + // Special-case ChoiceFormat. + if (formatter instanceof ChoiceFormat) + { + StringBuffer buf = new StringBuffer (); + // FIXME: don't actually know what is correct here. + // Can a sub-format refer to any argument, or just + // the single argument passed to it? Must test + // against JDK. + formatter.format(thisArg, buf, ignore); + MessageFormat mf = new MessageFormat (); + mf.setLocale(locale); + mf.applyPattern(buf.toString()); + formatter = mf; + } + formatter.format(thisArg, appendBuf, ignore); + } + + appendBuf.append(elements[i].trailer); + } + + return appendBuf; + } + + public final StringBuffer format (Object singleArg, StringBuffer appendBuf, + FieldPosition ignore) + { + Object[] args = new Object[1]; + args[0] = singleArg; + return format (args, appendBuf, ignore); + } + + public Format[] getFormats () + { + Format[] f = new Format[elements.length]; + for (int i = elements.length - 1; i >= 0; --i) + f[i] = elements[i].setFormat; + return f; + } + + public Locale getLocale () + { + return locale; + } + + public int hashCode () + { + // FIXME: not a very good hash. + return pattern.hashCode() + locale.hashCode(); + } + + private MessageFormat () + { + } + + public MessageFormat (String pattern) + { + applyPattern (pattern); + } + + public Object[] parse (String sourceStr, ParsePosition pos) + { + // Check initial text. + int index = pos.getIndex(); + if (! sourceStr.startsWith(leader, index)) + { + pos.setErrorIndex(index); + return null; + } + index += leader.length(); + + Vector results = new Vector (elements.length, 1); + // Now check each format. + for (int i = 0; i < elements.length; ++i) + { + Format formatter = null; + if (elements[i].setFormat != null) + formatter = elements[i].setFormat; + else if (elements[i].format != null) + formatter = elements[i].format; + + Object value = null; + if (formatter instanceof ChoiceFormat) + { + // We must special-case a ChoiceFormat because it might + // have recursive formatting. + ChoiceFormat cf = (ChoiceFormat) formatter; + String[] formats = (String[]) cf.getFormats(); + double[] limits = (double[]) cf.getLimits(); + MessageFormat subfmt = new MessageFormat (); + subfmt.setLocale(locale); + ParsePosition subpos = new ParsePosition (index); + + int j; + for (j = 0; value == null && j < limits.length; ++j) + { + subfmt.applyPattern(formats[j]); + subpos.setIndex(index); + value = subfmt.parse(sourceStr, subpos); + } + if (value != null) + { + index = subpos.getIndex(); + value = new Double (limits[j]); + } + } + else if (formatter != null) + { + pos.setIndex(index); + value = formatter.parseObject(sourceStr, pos); + if (value != null) + index = pos.getIndex(); + } + else + { + // We have a String format. This can lose in a number + // of ways, but we give it a shot. + int next_index = sourceStr.indexOf(elements[i].trailer, index); + if (next_index == -1) + { + pos.setErrorIndex(index); + return null; + } + value = sourceStr.substring(index, next_index); + index = next_index; + } + + if (value == null + || ! sourceStr.startsWith(elements[i].trailer, index)) + { + pos.setErrorIndex(index); + return null; + } + + if (elements[i].argNumber >= results.size()) + results.setSize(elements[i].argNumber + 1); + results.setElementAt(value, elements[i].argNumber); + + index += elements[i].trailer.length(); + } + + Object[] r = new Object[results.size()]; + results.copyInto(r); + return r; + } + + public Object[] parse (String sourceStr) throws ParseException + { + ParsePosition pp = new ParsePosition (0); + Object[] r = parse (sourceStr, pp); + if (r == null) + throw new ParseException ("couldn't parse string", pp.getErrorIndex()); + return r; + } + + public Object parseObject (String sourceStr, ParsePosition pos) + { + return parse (sourceStr, pos); + } + + public void setFormat (int variableNum, Format newFormat) + { + elements[variableNum].setFormat = newFormat; + } + + public void setFormats (Format[] newFormats) + { + if (newFormats.length < elements.length) + throw new IllegalArgumentException (); + int len = Math.min(newFormats.length, elements.length); + for (int i = 0; i < len; ++i) + elements[i].setFormat = newFormats[i]; + } + + public void setLocale (Locale loc) + { + locale = loc; + if (elements != null) + { + for (int i = 0; i < elements.length; ++i) + elements[i].setLocale(loc); + } + } + + public String toPattern () + { + return pattern; + } + + // The pattern string. + private String pattern; + // The locale. + private Locale locale; + // Variables. + private MessageFormatElement[] elements; + // Leader text. + private String leader; +} |