001    package railo.runtime.util;
002    
003    import java.math.RoundingMode;
004    import java.text.DecimalFormat;
005    import java.util.Locale;
006    
007    import railo.commons.lang.StringUtil;
008    
009    
010    /**
011     * Number formation class
012     */
013    public final class NumberFormat  {
014    
015            private static byte LEFT = 0;
016            private static byte CENTER = 1;
017            private static byte RIGHT = 2;
018            
019            /**
020             * formats a number
021             * @param number
022             * @return formatted number as string
023             */
024            public String format(Locale locale,double number) {
025                    
026                    DecimalFormat df=getDecimalFormat(locale);
027                    df.applyPattern(",0");
028                    df.setGroupingSize(3);
029                    
030                    
031                    return df.format(number).replace('\'',',');
032            }
033    
034            /**
035             * format a number with given mask
036             * @param number
037             * @param mask
038             * @return formatted number as string
039             * @throws InvalidMaskException
040             */
041            public String format(Locale locale,double number, String mask) throws InvalidMaskException  {
042                    byte justification = RIGHT;
043                    
044                    boolean useBrackets = false;
045                    boolean usePlus = false;
046                    boolean useMinus = false;
047                    boolean useDollar = false;
048                    boolean useComma = false;
049                    boolean foundDecimal = false;
050                    boolean symbolsFirst = false;
051                    boolean foundZero=false;
052                    
053                    int maskLen = mask.length();
054                    if(maskLen == 0) throw new InvalidMaskException("mask can't be a empty value");
055                    
056                    
057                    
058                    StringBuffer maskBuffer = new StringBuffer(mask);
059                    
060                            String mod=StringUtil.replace(mask, ",", "", true);
061                            if(StringUtil.startsWith(mod, '_'))symbolsFirst = true;
062                            if(mask.startsWith(",."))       {
063                                    maskBuffer.replace(0, 1, ",0");
064                            }
065                            //if(maskBuffer.charAt(0) == '.')maskBuffer.insert(0, '0');
066                    //print.out(maskBuffer);
067                    boolean addZero=false;
068                    for(int i = 0; i < maskBuffer.length();) {
069                            
070                            boolean removeChar = false;
071                            switch(maskBuffer.charAt(i)) {
072                            case '_':
073                            case '9':
074                                    if(foundDecimal || foundZero)   maskBuffer.setCharAt(i, '0');
075                                    else                                                    maskBuffer.setCharAt(i, '#');// #
076                            break;
077    
078                            case '.':
079                                    if(i>0 && maskBuffer.charAt(i-1)=='#')maskBuffer.setCharAt(i-1, '0');
080                                    if(foundDecimal)        removeChar = true;
081                                    else                            foundDecimal = true;
082                                    if(i==0)addZero=true;
083                            break;
084    
085                            case '(':
086                            case ')':
087                                    useBrackets = true;
088                                    removeChar = true;
089                            break;
090    
091                            case '+':
092                                    usePlus = true;
093                                    removeChar = true;
094                            break;
095    
096                            case '-':
097                                    useMinus = true;
098                                    removeChar = true;
099                            break;
100    
101                            case ',':
102                                    useComma = true;
103                                    if(true) {
104                                            removeChar = true;
105                                            maskLen++;
106                                    }
107                            break;
108    
109                            case 'L':
110                                    justification = LEFT;
111                                    removeChar = true;
112                            break;
113    
114                            case 'C':
115                                    justification = CENTER;
116                                    removeChar = true;
117                            break;
118    
119                            case '$':
120                                    useDollar = true;
121                                    removeChar = true;
122                            break;
123    
124                            case '^':
125                                    removeChar = true;
126                            break;
127    
128                            case '0':
129                                    if(!foundDecimal){
130                                            for(int y = 0; y < i;y++) {
131                                                    if(maskBuffer.charAt(y)=='#')
132                                                            maskBuffer.setCharAt(y, '0');
133                                            }
134                                    }
135                                    foundZero=true;
136                                    break;
137    
138                            default:
139                                throw new InvalidMaskException("invalid charcter ["+maskBuffer.charAt(i)+"], valid characters are ['_', '9', '.', '0', '(', ')', '+', '-', ',', 'L', 'C', '$', '^']");
140                            
141                            }
142                            if(removeChar) {
143                                    maskBuffer.deleteCharAt(i);
144                                    maskLen--;
145                            } 
146                            else {
147                                    i++;
148                            }
149                    }
150    
151                    if(addZero)
152                            maskBuffer.insert(0, '0');
153                    
154                    
155                    mask = new String(maskBuffer);
156                    maskLen=mask.length();
157                    DecimalFormat df = getDecimalFormat(locale);//(mask);
158                    int gs=df.getGroupingSize();
159                    df.applyPattern(mask);
160                    df.setGroupingSize(gs);
161                    df.setGroupingUsed(useComma);
162                    df.setRoundingMode(RoundingMode.HALF_UP);
163                    
164                    String formattedNum = df.format(StrictMath.abs(number));
165                    StringBuffer formattedNumBuffer = new StringBuffer(formattedNum);
166                    if(symbolsFirst) {
167                            int widthBefore = formattedNumBuffer.length();
168                            applySymbolics(formattedNumBuffer, number, usePlus, useMinus, useDollar, useBrackets);
169                            int offset = formattedNumBuffer.length() - widthBefore;
170                            
171                            if(formattedNumBuffer.length() < maskLen + offset) {
172                                    int padding = (maskLen + offset) - formattedNumBuffer.length();
173                                    applyJustification(formattedNumBuffer,justification, padding);
174                            }
175                                    
176                                    
177                            
178                    } 
179                    else {
180                            int widthBefore = formattedNumBuffer.length();
181                            
182                            StringBuffer temp = new StringBuffer(formattedNumBuffer.toString());
183                            applySymbolics(temp, number, usePlus, useMinus, useDollar, useBrackets);
184                            int offset = temp.length() - widthBefore;
185                            
186                            if(temp.length() < maskLen + offset) {
187                                    int padding = (maskLen + offset) - temp.length();
188                                    applyJustification(formattedNumBuffer,justification, padding);
189                            }
190                            applySymbolics(formattedNumBuffer, number, usePlus, useMinus, useDollar, useBrackets);
191                    }
192                    /*/ TODO better impl, this is just a quick fix
193                    formattedNum=formattedNumBuffer.toString();
194                     
195                    int index=formattedNum.indexOf('.');
196                    if(index==0) {
197                            formattedNumBuffer.insert(0, '0');
198                            formattedNum=formattedNumBuffer.toString();
199                    }
200                    else if(index>0){
201                            
202                    }
203                            
204                    String tmp=formattedNum.trim();
205                    if(tmp.length()>0 && tmp.charAt(0)=='.')
206                    */
207                    return formattedNumBuffer.toString();
208            }
209            
210    
211    
212            private void applyJustification(StringBuffer _buffer, int _just, int padding) {
213                    if(_just == CENTER)             centerJustify(_buffer, padding);
214                    else if(_just == LEFT)  leftJustify(_buffer, padding);
215                    else                                    rightJustify(_buffer, padding);
216            }
217    
218            private void applySymbolics(StringBuffer _buffer, double _no, boolean _usePlus, boolean _useMinus, boolean _useDollar, boolean _useBrackets) {
219                    if(_useBrackets && _no < 0.0D) {
220                            _buffer.insert(0, '(');
221                            _buffer.append(')');
222                    }
223                    if(_usePlus)
224                            _buffer.insert(0, _no <= 0.0D ? '-' : '+');
225                    if(_no < 0.0D && !_useBrackets && !_usePlus)
226                            _buffer.insert(0, '-');
227                    else
228                    if(_useMinus)
229                            _buffer.insert(0, ' ');
230                    if(_useDollar)
231                            _buffer.insert(0, '$');
232            }
233    
234            private void centerJustify(StringBuffer _src, int _padding) {
235                    int padSplit = _padding / 2 + 1;
236                    rightJustify(_src, padSplit);
237                    leftJustify(_src, padSplit);
238            }
239    
240            private void rightJustify(StringBuffer _src, int _padding) {
241                    for(int x = 0; x < _padding; x++)
242                            _src.insert(0, ' ');
243    
244            }
245    
246            private void leftJustify(StringBuffer _src, int _padding) {
247                    for(int x = 0; x < _padding; x++)
248                            _src.append(' ');
249    
250            }
251    
252            private DecimalFormat getDecimalFormat(Locale locale) {
253                    java.text.NumberFormat format = java.text.NumberFormat.getInstance(locale);
254                    if(format instanceof DecimalFormat) {
255                            return ((DecimalFormat)format);
256                            
257                    }
258                    return new DecimalFormat();
259            }
260    
261    }