/**************************************************************************
* Parts copyright (c) 2001 by Punch Telematix. All rights reserved. *
* Parts copyright (c) 2007, 2009 by Chris Gray, /k/ Embedded Java *
* Solutions. All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of Punch Telematix or of /k/ Embedded Java Solutions*
* nor the names of other contributors may be used to endorse or promote*
* products derived from this software without specific prior written *
* permission. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL PUNCH TELEMATIX, /K/ EMBEDDED JAVA SOLUTIONS OR OTHER *
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
package java.text;
import java.util.Date;
import java.util.Locale;
import java.util.Arrays;
import java.util.StringTokenizer;
public class MessageFormat extends Format {
private static final long serialVersionUID = 6479157306784022952L;
private static final FieldPosition TRASHPOSITION = new FieldPosition(0);
//dictated by the serialized form
private int[] argumentNumbers = new int[10];
private Format[] formats = new Format[10];
private Locale locale = Locale.getDefault();
private int maxOffset;
private int[] offsets = new int[10];
private String pattern;
private transient String[] subPatterns;
private transient String origPattern;
public MessageFormat(String pattern) throws IllegalArgumentException {
applyPattern(pattern);
}
public MessageFormat(String pattern, Locale locale) throws IllegalArgumentException {
this.locale = locale;
applyPattern(pattern);
}
private Format getDateTimeFormat(StringTokenizer st, boolean time){
int style = DateFormat.DEFAULT;
if(st.hasMoreTokens()){
String token = st.nextToken();
if(token.equals("short")){
style = DateFormat.SHORT;
}
else if (token.equals("medium")){
style = DateFormat.MEDIUM;
}
else if (token.equals("long")){
style = DateFormat.LONG;
}
else if (token.equals("full")){
style = DateFormat.FULL;
}
else{
return new SimpleDateFormat(token, locale);
}
}
if(time){
return DateFormat.getTimeInstance(style, locale);
}
else {
return DateFormat.getDateInstance(style, locale);
}
}
private int applyFormat(int idx, String pattern){
int end = pattern.indexOf('}', idx);
int last = pattern.indexOf('{', idx);
while (last < end && last != -1 && end != -1){
//System.out.println("in while last = "+last+" <--> end = "+end);
end = pattern.indexOf('}', end+1);
last = pattern.indexOf('{', last+1);
}
//System.out.println("after while last = "+last+" <--> end = "+end);
if(end == -1 || end == idx){
throw new IllegalArgumentException("bad pattern");
}
StringTokenizer st = new StringTokenizer(pattern.substring(idx,end),",");
try {
argumentNumbers[++maxOffset] = Integer.parseInt(st.nextToken());
if(argumentNumbers[maxOffset]<0 || argumentNumbers[maxOffset]>9){
throw new IllegalArgumentException("bad pattern: bad argument number "+argumentNumbers[maxOffset]);
}
if(st.hasMoreTokens()){
String token = st.nextToken().trim();
//System.out.println("looking for '"+token+"'");
if(token.equals("time")){
formats[maxOffset] = getDateTimeFormat(st,true);
}
else if (token.equals("date")){
formats[maxOffset] = getDateTimeFormat(st,true);
}
else if (token.equals("number")){
if(st.hasMoreTokens()){
token = st.nextToken();
//System.out.println("looking for '"+token+"'");
String trimmed = token.trim();
if(trimmed.equals("currency")){
formats[maxOffset] = NumberFormat.getCurrencyInstance(locale);
}
else if(trimmed.equals("percent")){
formats[maxOffset] = NumberFormat.getPercentInstance(locale);
}
else if(trimmed.equals("integer")){
NumberFormat nf = NumberFormat.getNumberInstance(locale);
nf.setParseIntegerOnly(true);
formats[maxOffset] = nf;
}
else {
formats[maxOffset] = new DecimalFormat(token, new DecimalFormatSymbols(locale));
}
}
else {
formats[maxOffset] = NumberFormat.getInstance(locale);
}
}
else if (token.equals("choice")){
if(st.hasMoreTokens()){
formats[maxOffset] = new ChoiceFormat(st.nextToken());
}
else{
formats[maxOffset] = new ChoiceFormat("0# ");
}
}
else {
throw new IllegalArgumentException("bad pattern");
}
}
else{
formats[maxOffset] = null;
}
}
catch(RuntimeException rt){
throw new IllegalArgumentException("bad pattern");
}
return end;
}
public void applyPattern(String pattern) throws IllegalArgumentException {
//System.out.println("applying pattern '"+pattern+"'");
int len = pattern.length();
maxOffset = -1;
StringBuffer pat = new StringBuffer(len);
for (int i = 0 ; i < len ; i++){
char ch = pattern.charAt(i);
if(ch == '\'' && (++i) < len){
pat.append(pattern.charAt(i));
}
else if(ch == '{'){
i = applyFormat(i+1, pattern);
offsets[maxOffset] = pat.length();
}
else
pat.append(ch);
}
subPatterns = null;
origPattern = pattern;
this.pattern = pat.toString();
}
public Object clone() {
MessageFormat mf = (MessageFormat) super.clone();
mf.argumentNumbers = (int[])argumentNumbers.clone();
mf.offsets = (int[]) offsets;
mf.formats = (Format[]) formats.clone();
return mf;
}
public boolean equals(Object o){
if(!(o instanceof MessageFormat)){
return false;
}
MessageFormat mf = (MessageFormat) o;
return this.pattern.equals(mf.pattern)
&& this.maxOffset == mf.maxOffset
&& this.locale.equals(mf.locale)
&& Arrays.equals(this.formats ,mf.formats)
&& Arrays.equals(this.offsets ,mf.offsets)
&& Arrays.equals(this.argumentNumbers ,mf.argumentNumbers);
}
public static String format(String pattern, Object[] args){
return new MessageFormat(pattern).format(args,new StringBuffer(),null).toString();
}
public final StringBuffer format(Object[] args, StringBuffer dest, FieldPosition pos){
if(subPatterns == null){
createSubPatterns();
}
for(int i = 0 ; i <= maxOffset ; i++){
Format format = formats[i];
dest.append(subPatterns[i]);
if(format == null){
Object o = args[argumentNumbers[i]];
if(o instanceof Date){
DateFormat.getDateTimeInstance(DateFormat.DEFAULT,DateFormat.DEFAULT,locale).format((Date)o,dest,TRASHPOSITION);
}
else if(o instanceof Number){
NumberFormat.getInstance(locale).format(((Number)o).doubleValue(), dest,TRASHPOSITION);
}
else {
dest.append(o);
}
}
else{
//System.out.println("formatting '"+args[argumentNumbers[i]]+"'using "+format);
/*
if(format instanceof DecimalFormat){
((DecimalFormat)format).debug(origPattern);
}*/
format.format(args[argumentNumbers[i]], dest, TRASHPOSITION);
//System.out.println("formatted '"+args[argumentNumbers[i]]+"'using "+format+" got '"+dest+"'");
}
}
dest.append(subPatterns[maxOffset+1]);
return dest;
}
public final StringBuffer format(Object obj, StringBuffer dest, FieldPosition pos){
if(obj instanceof Object[]){
return format((Object[])obj, dest, pos);
}
Object[] args = new Object[1];
args[0] = obj;
return format(args, dest, pos);
}
public Format[] getFormats(){
return formats;
}
public Locale getLocale(){
return locale;
}
public int hashCode(){
return locale.hashCode() ^ pattern.hashCode() ^ maxOffset;
}
public Object[] parse(String src, ParsePosition pos) {
if(subPatterns == null){
createSubPatterns();
}
Object[] res = new Object[10];
int p = pos.getIndex();
for(int i = 0 ; i <= maxOffset ; i++){
String s = subPatterns[i];
int len = s.length();
if(!src.regionMatches(p, s, 0, len)){
pos.setErrorIndex(p);
//System.out.println("PARSE FAILED: missing subpattern "+subPatterns[i]);
return null;
}
p += len;
Format format = formats[i];
if(format == null){
int end = src.indexOf(subPatterns[i+1],p);
if(end == -1){
pos.setErrorIndex(p);
//System.out.println("PARSE FAILED: missing subpattern after "+subPatterns[i]);
return null;
}
res[argumentNumbers[i]] = src.substring(p,end);
p = end;
}
else {
pos.setIndex(p);
Object o = format.parseObject(src, pos);
if(o == null){
//System.out.println("PARSE FAILED: parsing failed "+format);
return null;
}
res[argumentNumbers[i]] = o;
p = pos.getIndex();
}
}
String s = subPatterns[maxOffset+1];
int len = s.length();
if(!src.regionMatches(p, s, 0, len)){
pos.setErrorIndex(p);
//System.out.println("PARSE FAILED: missing subpattern "+s);
return null;
}
pos.setIndex(p+len);
return res;
}
public Object[] parse(String src) throws ParseException {
ParsePosition pos = new ParsePosition(0);
Object[] res = parse(src,pos);
if(res == null){
throw new ParseException("parsing failed!",pos.getErrorIndex());
}
return res;
}
public Object parseObject(String source, ParsePosition pos) {
return parse(source, pos);
}
public void setFormat(int num, Format format){
formats[num] = format;
}
public void setFormats(Format[] formats){
this.formats = formats;
}
public void setLocale(Locale loc){
locale = loc;
}
public String toPattern(){
if(origPattern == null){
if(subPatterns == null){
createSubPatterns();
}
StringBuffer pattern = new StringBuffer();
for(int i = 0 ; i <= maxOffset ; i++){
addQuotes(subPatterns[i], pattern);
pattern.append('{');
pattern.append(argumentNumbers[i]);
Format format = formats[i];
if(format != null){
if(format instanceof ChoiceFormat){
pattern.append(",choice,");
pattern.append(((ChoiceFormat)format).toPattern());
}
else if(format instanceof DecimalFormat){
pattern.append(",number,");
pattern.append(((DecimalFormat)format).toPattern());
}
else if(format instanceof SimpleDateFormat){
pattern.append(",date,");
pattern.append(((SimpleDateFormat)format).toPattern());
}
}
pattern.append('}');
}
addQuotes(subPatterns[maxOffset+1], pattern);
origPattern = pattern.toString();
}
return origPattern;
}
private void addQuotes(String pat, StringBuffer dest){
String sep = "'{";
StringTokenizer st = new StringTokenizer(pat, sep, true);
while(st.hasMoreTokens()){
String token = st.nextToken();
if(sep.indexOf(token) != -1){
dest.append('\'');
}
dest.append(token);
}
}
private void createSubPatterns(){
subPatterns = new String[maxOffset+2];
int prev = 0;
for(int i = 0 ; i <= maxOffset ; i++){
int off = offsets[i];
subPatterns[i] = pattern.substring(prev, off);
prev = off;
}
subPatterns[maxOffset+1] = pattern.substring(prev);
}
private void debug(){
System.out.println("ORIGINAL PATTERN = '"+origPattern+"'");
for (int i=0 ; i <= maxOffset ; i++){
System.out.println("argNr "+argumentNumbers[i]+" using "+formats[i]+" off "+offsets[i]);
}
}
}