/************************************************************************* * Copyright (c) 2001 by Acunia N.V. All rights reserved. * * * * This software is copyrighted by and is the sole property of Acunia N.V. * * and its licensors, if any. All rights, title, ownership, or other * * interests in the software remain the property of Acunia N.V. and its * * licensors, if any. * * * * This software may only be used in accordance with the corresponding * * license agreement. Any unauthorized use, duplication, transmission, * * distribution or disclosure of this software is expressly forbidden. * * * * This Copyright notice may not be removed or modified without prior * * written consent of Acunia N.V. * * * * Acunia N.V. reserves the right to modify this software without notice. * * * * Acunia N.V. * * Vanden Tymplestraat 35 info@acunia.com * * 3000 Leuven http://www.acunia.com * * Belgium - EUROPE * **************************************************************************/ /* ** $Id: DecimalFormat.java,v 1.1.1.1 2004/07/12 14:07:47 cvs Exp $ */ package java.text; //import java.util.Arrays; //import java.util.StringTokenizer; import java.io.IOException; import java.io.ObjectInputStream; public class DecimalFormat extends NumberFormat { private static final DecimalFormatSymbols DEFAULT = new DecimalFormatSymbols(); /** ** dictated by the serialized form ... */ private static final long serialVersionUID = 864413376551465018L; private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); if(serialVersionOnStream < 1){ useExponentialNotation = false; serialVersionOnStream = 1; } } private boolean decimalSeparatorAlwaysShown; private byte groupingSize = 3; private byte minExponentDigits; private int multiplier = 1; private String negativePrefix = "-"; private String negativeSuffix = ""; private String positivePrefix = ""; private String positiveSuffix = ""; private int serialVersionOnStream = 1; private DecimalFormatSymbols symbols; private boolean useExponentialNotation; public DecimalFormat(){ symbols = new DecimalFormatSymbols(); super.setMaximumFractionDigits(340); super.setMaximumIntegerDigits(309); super.setMinimumIntegerDigits(1); } DecimalFormat(DecimalFormatSymbols dfs){ symbols = dfs; } public DecimalFormat(String pattern){ this(pattern, new DecimalFormatSymbols()); } public DecimalFormat(String pattern, DecimalFormatSymbols dfs){ applyPattern(pattern, dfs); super.setMaximumIntegerDigits(309); symbols = dfs; //debug(pattern); } public void applyLocalizedPattern(String pattern){ applyPattern(pattern, symbols); //debug(pattern); } public void applyPattern(String pattern){ applyPattern(pattern, DEFAULT); //debug(pattern); } public Object clone(){ DecimalFormat df = (DecimalFormat) super.clone(); df.symbols = (DecimalFormatSymbols) symbols.clone(); return df; } public boolean equals(Object o){ if(!(o instanceof DecimalFormat)){ return false; } DecimalFormat df = (DecimalFormat)o; return this.symbols.equals(df.symbols) && this.decimalSeparatorAlwaysShown == df.decimalSeparatorAlwaysShown && this.minExponentDigits == df.minExponentDigits && this.multiplier == df.multiplier && this.negativePrefix.equals(df.negativePrefix) && this.negativeSuffix.equals(df.negativeSuffix) && this.positivePrefix.equals(df.positivePrefix) && this.positiveSuffix.equals(df.positiveSuffix) && this.useExponentialNotation == df.useExponentialNotation; } public StringBuffer format(double number, StringBuffer dest, FieldPosition pos){ //TODO: take min en max digits into account !!! //System.out.println("formatting number '"+number+"'"); boolean neg = number < 0.0; double d = 0.5 * Math.pow(10,-getMaximumFractionDigits()); number = (number * multiplier) + (neg ? -d : d); //System.out.println("formatting number '"+number+"' "+getMaximumFractionDigits()); String value = String.valueOf(number); if(neg){ value = value.substring(1); } int p = value.indexOf('E'); if(useExponentialNotation){ if(p == -1){ value = insertExpononent(value); } return formatExponent(value, dest, pos, neg); } else if(p != -1){// we don't need a an Expononent StringBuffer buf = new StringBuffer(128); int exp = Integer.parseInt(value.substring(p+1)); p = value.indexOf('.'); if(exp < 0){ //we have p digits before the '.' int add = 1-p-exp; addChars('0', add, buf); buf.append(value); p += (add < 0 ? 0 : add); buf.deleteCharAt(p); buf.insert(p+exp,'.'); } else { int add = exp + value.length() - p; buf.append(value); addChars('0', add, buf); buf.deleteCharAt(p); buf.insert(p+exp,'.'); } value = buf.toString(); } return format(value, dest, pos, neg); } public StringBuffer format(long number, StringBuffer dest, FieldPosition pos){ //System.out.println("formatting number '"+number+"'"); boolean neg = number < 0; String value = String.valueOf(number * multiplier)+'.'; if(neg){ value = value.substring(1); } if(useExponentialNotation){ formatExponent(insertExpononent(value), dest, pos, neg); } return format(value, dest, pos, neg); } private StringBuffer format(String number, StringBuffer dest, FieldPosition pos, boolean neg){ //System.out.println("formatting number '"+number+"'"); dest.append((neg ? negativePrefix : positivePrefix)); int start = dest.length(); int p = number.indexOf('.'); int min = getMinimumIntegerDigits(); if(min > p){ //we need to add zeros addChars(symbols.getZeroDigit(), min - p, dest); dest.append(number); p = min; } else { dest.append(number); int max = getMaximumIntegerDigits(); if(max < p){ dest.delete(start,start + p - max); p = max; } } if(min == 0 && p == 1 && dest.charAt(start) == '0'){ p--; dest.deleteCharAt(start); } if(isGroupingUsed()){ min = (p-1) / groupingSize; int in = p+start; char ch = symbols.getGroupingSeparator(); for(int i = 0 ; i < min ; i++){ in -= groupingSize; dest.insert(in,ch); } p += min; } min = getMinimumFractionDigits(); int frac = dest.length() - start - p - 1; if(min > frac){ //we need to add zeros addChars(symbols.getZeroDigit(), min - frac, dest); } else { int max = getMaximumFractionDigits(); if(max < frac){ dest.setLength(start + p + max + 1); } } if(!decimalSeparatorAlwaysShown){//check and remove dot if needed boolean cut = true; for(int i = p+1+start ; i < dest.length() ; i++){ if(dest.charAt(i) != '0'){ cut = false; break; } } if(cut){ dest.setLength(p+start); } } switch(pos.getField()){ case FRACTION_FIELD: pos.setBeginIndex(start+p+1); pos.setEndIndex(dest.length()); break; case INTEGER_FIELD: pos.setBeginIndex(start); pos.setEndIndex(start+p); break; default: } int stop = dest.length(); dest.append((neg ? negativeSuffix : positiveSuffix)); return dest; } /** ** for now scientific format is not supported */ private StringBuffer formatExponent(String number, StringBuffer dest, FieldPosition pos, boolean neg){ //TODO if needed ... throw new UnsupportedOperationException("No exponential formats supported yet"); } /** ** for now scientific format is not supported */ private String insertExpononent(String s){ //TODO if needed ... return s; } public DecimalFormatSymbols getDecimalFormatSymbols(){ return symbols; } public int getGroupingSize(){ return groupingSize; } public int getMultiplier(){ return multiplier; } public String getNegativePrefix(){ return negativePrefix; } public String getNegativeSuffix(){ return negativeSuffix; } public String getPositivePrefix(){ return positivePrefix; } public String getPositiveSuffix(){ return positiveSuffix; } public int hashCode(){ int hash = (decimalSeparatorAlwaysShown ? 0xaaaaaaaa : 0x55555555); hash ^= (useExponentialNotation ? 0xcccccccc : 0x33333333); return symbols.hashCode() ^ minExponentDigits ^ multiplier ^ negativePrefix.hashCode() ^ negativeSuffix.hashCode() ^ positivePrefix.hashCode() ^ positiveSuffix.hashCode(); } public boolean isDecimalSeparatorAlwaysShown(){ return decimalSeparatorAlwaysShown; } public Number parse(String str, ParsePosition pos){ Number Num = null; //System.out.println("parsing '"+str+"'"); int p = pos.getIndex(); int len = str.length(); while(p < len){ if(Character.isWhitespace(str.charAt(p))){ p++; } else{ break; } } try { //first try to find out if we have a prefix .. boolean positive = true; boolean returnlong = true; int preflen = -1; if(str.regionMatches(p, positivePrefix, 0, positivePrefix.length())){ //System.out.println("matched positivePrefix '"+positivePrefix+"'"); preflen = positivePrefix.length(); } if(str.regionMatches(p, negativePrefix, 0, negativePrefix.length())){ //System.out.println("matched positivePrefix '"+positivePrefix+"'"); if(preflen < negativePrefix.length()){ positive = false; preflen = negativePrefix.length(); } } if(preflen == -1){ //System.out.println("no valid Prefix found ..."); pos.setErrorIndex(p); } else { p += preflen; //System.out.println("found prefix '"+str.substring(0,preflen)+"' "+p+" "+positive); StringBuffer buf = new StringBuffer(64); p = parseIntegerPart(buf,str,p); //System.out.println("found Integer part '"+buf+"' "+p); if(p < 0){ pos.setErrorIndex(-p); } else { //parse Fraction ... if(p < len && str.charAt(p) == symbols.getDecimalSeparator() & !isParseIntegerOnly()){ int start = ++p; buf.append('.'); int count = 0; while(p< len && count < maximumFractionDigits){ char ch = str.charAt(p); //System.out.println("char is "+ch+", pos = "+p+", count = "+count+" string = "+str); if(ch >= '0' && ch <= '9'){ buf.append(ch); count++; p++; } else { break; } } //check minimum ... if(count < minimumFractionDigits){ pos.setErrorIndex(p); //System.out.println("returning ERROR -- fraction to small size = "+count+", but needed "+minimumFractionDigits); return null; } if(start < p){ returnlong = false; } else{ buf.setLength(buf.length()-1); } } //System.out.println("found number part '"+buf+"' "+p); //parse Suffix ... preflen = -1; //System.out.println("string '"+str+"' "+p+", '"+positivePrefix+"'"); //System.out.println("trying to match '"+str.substring(p)+"' ? '"+positiveSuffix+"' , '"+negativeSuffix+"'"); if(str.regionMatches(p, positiveSuffix, 0, positiveSuffix.length())){ //System.out.println("matched positiveSuffix '"+positiveSuffix+"'"); if(!positive && !positiveSuffix.equals(negativeSuffix) && positiveSuffix.length() > 0){ pos.setErrorIndex(p); //System.out.println("returning ERROR"); return null; } preflen = positiveSuffix.length(); } if(str.regionMatches(p, negativeSuffix, 0, negativeSuffix.length())){ //System.out.println("matched negativeSuffix '"+negativeSuffix+"'"); if(preflen < negativeSuffix.length()){ if(positive && !positivePrefix.equals(negativePrefix)){ pos.setErrorIndex(p); return null; } positive = false; preflen = negativeSuffix.length(); } } if(preflen == -1){ //System.out.println("no valid Suffix found ..."); pos.setErrorIndex(p); return null; } p += preflen; if(!positive){ buf.insert(0,'-'); } if(returnlong){ Num = new Long(Long.parseLong(buf.toString())*multiplier); } else{ Num = new Double(Double.parseDouble(buf.toString())*multiplier); } pos.setIndex(p); } } } catch(RuntimeException rt){ pos.setErrorIndex(p); //System.out.println("Parsing failed returning 'null'"); //rt.printStackTrace(); return null; } return Num; } private int parseIntegerPart(StringBuffer buf, String number, int pos){ int len = number.length(); int count=0; if(isGroupingUsed()){ //System.out.println("grouping is used ... "+pos); char sep = symbols.getGroupingSeparator(); int group = 0; boolean first = true; while(pos < len && count < maximumIntegerDigits){ char ch = number.charAt(pos); if(ch >= '0' && ch <= '9'){ if(group == groupingSize){ return -pos; } buf.append(ch); count++; group++; } else if(ch == sep){ if(first || group == groupingSize){ first = false; group = 0; } else{ return -pos; } } else { break; } pos++; } } else{ //System.out.println("grouping is not used ... "+pos); while(pos < len && count < maximumIntegerDigits){ char ch = number.charAt(pos); //System.out.println("char "+pos+" is "+ch); if(ch >= '0' && ch <= '9'){ buf.append(ch); count++; pos++; } else { break; } } } if(count < minimumIntegerDigits){ //System.out.println("RETURNING ERROR: minimum not reached in '"+number+"' at pos"+pos+" --> "+count+" <--> "+minimumIntegerDigits); //debug("patter unknown"); return -pos; } return pos; } public void setDecimalFormatSymbols(DecimalFormatSymbols syms){ symbols = syms; } public void setDecimalSeparatorAlwaysShown(boolean val){ decimalSeparatorAlwaysShown = val; } public void setGroupingSize(int size){ this.groupingSize = (byte)size; } public void setMaximumFractionDigits(int max){ if(max >= 340){ max = 340; } super.setMaximumFractionDigits(max); } public void setMaximumIntegerDigits(int max){ if(max >= 309){ max = 309; } super.setMaximumIntegerDigits(max); } public void setMinimumFractionDigits(int min){ if(min >= 340){ min = 340; } super.setMinimumFractionDigits(min); } public void setMinimumIntegerDigits(int min){ if(min >= 309){ min = 309; } super.setMinimumIntegerDigits(min); } public void setMultiplier(int mul){ this.multiplier = mul; } public void setNegativePrefix(String prefix){ this.negativePrefix = prefix; } public void setNegativeSuffix(String suffix){ this.negativeSuffix = suffix; } public void setPositivePrefix(String prefix){ this.positivePrefix = prefix; } public void setPositiveSuffix(String suffix){ this.positiveSuffix = suffix; } public String toLocalizedPattern(){ return toPattern(symbols); } public String toPattern(){ return toPattern(DEFAULT); } private String toPattern(DecimalFormatSymbols dfs){ StringBuffer buf = new StringBuffer(128); buf.append(positivePrefix); int pos = buf.length(); int min = getMinimumIntegerDigits(); addChars(dfs.getZeroDigit(), min, buf); if(isGroupingUsed()){ addChars(dfs.getDigit(), groupingSize + 1 - min, buf); int i = 1; if (min > groupingSize){ i = min / groupingSize; min += pos; } else { min = groupingSize + 1; } char ch = dfs.getGroupingSeparator(); for(int j = 0 ; j < i ; j++){ min -= groupingSize; buf.insert(min,ch); } } min = getMinimumFractionDigits(); int max = getMaximumFractionDigits(); if(decimalSeparatorAlwaysShown || min > 0 || max > 0){ buf.append(dfs.getDecimalSeparator()); addChars(dfs.getZeroDigit(), min, buf); addChars(dfs.getDigit(), max - min, buf); } String format = buf.substring(pos); buf.append(positiveSuffix); if(!"-".equals(negativePrefix) || !"".equals(negativeSuffix)){ buf.append(dfs.getPatternSeparator()); buf.append(negativePrefix); buf.append(format); buf.append(negativeSuffix); } return buf.toString(); } private void addChars(char digit, int size, StringBuffer buf){ for(int i = 0 ; i < size ; i++){ buf.append(digit); } } private void applyPattern(String pattern, DecimalFormatSymbols dfs){ String specials = dfsToString(dfs); //System.out.println("applying pattern '"+pattern+"'"); //step 1: get the Prefix StringBuffer buf = new StringBuffer(); int pos = locateString(0, pattern, specials, buf); positivePrefix = buf.toString(); //System.out.println("prefix 1'"+positivePrefix+"' "+pos); //step 2: get the Integer Part pos = locateIntegerPart(pos, pattern, true, dfs); //step 3: get the Fraction Part pos = locateFractionPart(pos, pattern, true, dfs); //step 4: get the suffix buf.setLength(0); if(pos < pattern.length()){ char ch = pattern.charAt(pos); if(ch == dfs.getPercent()){ //System.out.println("percent sign found "+pos); multiplier = 100; buf.append(ch); pos++; } else if(ch == dfs.getPerMill()){ //System.out.println("permill sign found "+pos); multiplier = 1000; buf.append(ch); pos++; } else { multiplier = 1; pos = locateString(pos, pattern, specials, buf); } //System.out.println("setting positiveSuffix to '"+buf+"' "+pos); positiveSuffix = buf.toString(); if(pos < pattern.length() && pattern.charAt(pos) == dfs.getPatternSeparator()){ //System.out.println("found a second pattern"); pos++; buf.setLength(0); pos = locateString(pos, pattern, specials, buf); //System.out.println("prefix2 '"+buf+"' "+pos); negativePrefix = buf.toString(); pos = locateIntegerPart(pos, pattern, false, dfs); pos = locateFractionPart(pos, pattern, false, dfs); buf.setLength(0); pos = locateString(pos, pattern, specials, buf); if (multiplier > 1){ //System.out.println("setting negativeSuffix to '"+positiveSuffix+"'"); negativeSuffix = positiveSuffix; } else { //System.out.println("setting negativeSuffix to '"+positiveSuffix+"'"); negativeSuffix = buf.toString(); } } else { if(multiplier > 1){ //System.out.println("setting Suffixes to '"+positiveSuffix+"'"); negativeSuffix = positiveSuffix; } negativePrefix = "-"; } } else { positiveSuffix = ""; negativeSuffix = positiveSuffix; negativePrefix = "-"; multiplier = 1; } } /** ** tries to locate the prefix in the string starting from start ... */ private int locateString(int start, String pattern, String specials, StringBuffer buf){ int stop = pattern.length(); //System.out.println("looking for String at "+start); for(int i = start ; i < stop ; i++){ char ch = pattern.charAt(i); //System.out.println("character at "+i+" is '"+ch+"'"); if(ch == '\''){ if(i+2 < stop && pattern.charAt(i+2) == '\''){ buf.append(pattern.charAt(i+1)); i += 2; } } else if(specials.indexOf(ch) != -1){ //System.out.println("stopped looking for String at "+i); return i; } else { buf.append(ch); } } //System.out.println("stopped looking for String at "+stop); return stop; } /** ** locates the IntegerPart in the pattern ** */ private int locateIntegerPart(int start, String pattern, boolean set, DecimalFormatSymbols dfs){ int min = 0; int size = -1; int stop = pattern.length(); char digit = dfs.getDigit(); char zero = dfs.getZeroDigit(); char group = dfs.getGroupingSeparator(); //System.out.println("looking for integer part at "+start); for(int i = start ; i < stop ; i++){ char ch = pattern.charAt(i); //System.out.println("character at "+i+" is '"+ch+"' "+size); if (ch == digit){ if(min > 0){ throw new IllegalArgumentException("bad pattern"); } } else if(ch == zero){ min++; } else if(ch == group){ size=0; continue; } else { stop = i; } if(size != -1){ if(stop != i){ size++; } else if(size == 0){ throw new IllegalArgumentException("bad pattern"); } } } if(set){ setMinimumIntegerDigits(min); if(size != -1){ setGroupingUsed(true); groupingSize = (byte)size; if(groupingSize < 0){ groupingSize = Byte.MAX_VALUE; } } else{ setGroupingUsed(false); groupingSize = 0; } } //System.out.println("stopped looking for integer part at "+stop); return stop; } /** ** locates the Fraction Part in the pattern ** */ private int locateFractionPart(int start, String pattern, boolean set, DecimalFormatSymbols dfs){ int stop = pattern.length(); //System.out.println("looking for fraction part at "+start); if(start >= stop || pattern.charAt(start) != dfs.getDecimalSeparator()){ if(set){ //System.out.println("No fraction found !"); super.setMaximumFractionDigits(0); decimalSeparatorAlwaysShown = false; } return start; } int min = 0; int digits = 0; char digit = dfs.getDigit(); char zero = dfs.getZeroDigit(); for(int i = start+1 ; i < stop ; i++){ char ch = pattern.charAt(i); if(ch == digit){ digits++; } else if(ch == zero){ if(digits > 0){ throw new IllegalArgumentException("bad pattern"); } min++; } else { stop = i; } } if(set){ setMinimumFractionDigits(min); setMaximumFractionDigits(min+digits); decimalSeparatorAlwaysShown = (min > 0); } //System.out.println("stopped looking for fraction part at "+stop+" "+set+" "+min+" "+digits); return stop; } /** ** builds a string containing all special characters that end a prefix ** */ private String dfsToString(DecimalFormatSymbols dfs){ StringBuffer buf = new StringBuffer(); buf.append(dfs.getDigit()); buf.append(dfs.getZeroDigit()); buf.append(dfs.getDecimalSeparator()); buf.append(dfs.getGroupingSeparator()); buf.append(dfs.getPatternSeparator()); buf.append(dfs.getPercent()); buf.append(dfs.getPerMill()); return buf.toString(); } //DEBUGGING purpose only ... void debug(String pattern){ System.out.println( "applied '"+pattern+"' on "+this.toString() +"\ndecimalSeparatorAlwaysShown "+decimalSeparatorAlwaysShown +"\ngroupingSize "+ groupingSize +"\nmultiplier "+ multiplier +"\nnegativePrefix "+ negativePrefix +"\nnegativeSuffix "+ negativeSuffix +"\npositivePrefix "+ positivePrefix +"\npositiveSuffix "+ positiveSuffix +"\nDecimalFormatSymbols "+dfsToString(symbols) +"\nuseExponentialNotation "+useExponentialNotation +"\ngetMaximumFractionDigits "+getMaximumFractionDigits() +"\ngetMinimumFractionDigits "+getMinimumFractionDigits() +"\ngetMaximumIntegerDigits "+getMaximumIntegerDigits() +"\ngetMinimumIntegerDigits "+getMinimumIntegerDigits() ); } }