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