/* * Copyright (C) 2000 - 2008 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ */ package com.naryx.tagfusion.expression.function; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.List; import java.util.Locale; import com.naryx.tagfusion.cfm.engine.cfData; import com.naryx.tagfusion.cfm.engine.cfJavaObjectData; import com.naryx.tagfusion.cfm.engine.cfSession; import com.naryx.tagfusion.cfm.engine.cfStringData; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; import com.naryx.tagfusion.cfm.engine.dataNotSupportedException; public class numberFormat extends functionBase { private static final long serialVersionUID = 1L; private static byte LEFT = 0, CENTER = 1, RIGHT = 2; public numberFormat(){ min = 1; max = 2; } public String[] getParamInfo(){ return new String[]{ "date1", "format mask - default #,###" }; } public java.util.Map getInfo(){ return makeInfo( "format", "Formats a number to the given format mask", ReturnType.STRING ); } public cfData execute( cfSession _session, List<cfData> parameters )throws cfmRunTimeException{ if ( parameters.size() == 1 ){ return defaultExecute( _session, parameters, new DecimalFormatSymbols( Locale.US ) ); }else{ return mainExecute( _session, parameters, new DecimalFormatSymbols( Locale.US ), Locale.US ); } } protected cfData defaultExecute( cfSession _session, List<cfData> parameters, DecimalFormatSymbols _dfs )throws cfmRunTimeException{ double val = getFormatValue( _session, parameters.get(0) ); DecimalFormat DF = new DecimalFormat("#,###", _dfs); DF.setGroupingSize( 3 ); return new cfStringData( DF.format( val ) ); } private double getFormatValue( cfSession _session, cfData _param ) throws cfmRunTimeException{ double val = 0; if ( _param.getDataType() == cfData.CFJAVAOBJECTDATA && ( (cfJavaObjectData) _param ).getInstance() instanceof Number ){ val = ( (Number) ( (cfJavaObjectData) _param ).getInstance() ).doubleValue(); }else{ try{ val = _param.getDouble(); }catch(dataNotSupportedException e){ String s = _param.getString(); if(s.trim().length() > 0) throwException(_session, "Parameter \"" + _param.getString() + "\" is not a numeric."); } } return val; } // use if parameters.size() == 2 protected cfData mainExecute( cfSession _session, List<cfData> parameters, DecimalFormatSymbols _dfs, Locale _locale )throws cfmRunTimeException{ byte justification = RIGHT; // the mask to use for formating String mask = parameters.get(0).getString(); double val = getFormatValue( _session, parameters.get(1) ); boolean useBrackets = false; // set when a '(' or ')' is found in the mask boolean usePlus = false; // set when a '+' is found in the mask boolean useMinus = false; // set when a '-' is found in the mask boolean useDollar = false; // set when a '$' is found in the mask boolean useComma = false; // set when a comma is found in the mask boolean foundDecimal = false; // indicates that the first '.' has been found boolean symbolsFirst = true; // used to decide whether to justify or apply symbols first boolean foundZero = false; // set when a 0 found int maskLength = mask.length(); // used to pad the string with spaces at the end if ( maskLength == 0 ){ throwException( _session, "Invalid mask. The mask provided was 0 chars in length." ); } // preprocess - if one '0' then replace all '_' with '0' (but only if the '0' appears after any '.') int zeroIndx = mask.indexOf( '0' ); if ( zeroIndx != -1 ){ int periodIndx = mask.indexOf( '.' ); if ( periodIndx == -1 || periodIndx > zeroIndx ) mask = mask.replace( '_', '0' ); } StringBuilder maskBuffer = new StringBuilder( mask ); if ( maskBuffer.charAt( 0 ) == '_' && maskLength > 1 && ( maskBuffer.charAt( 1 ) == '$' || maskBuffer.charAt( 1 ) == '+' || maskBuffer.charAt( 1 ) == '-' || maskBuffer.charAt( 1 ) == '(' ) ) { symbolsFirst = true; } else { for ( int i = 0; i < maskBuffer.length(); i++ ){ char nextCh = maskBuffer.charAt(i); if ( nextCh == '_' || nextCh == '+' || nextCh == '(' ){ symbolsFirst = false; break; } } } int zeroCount = 0; // keep a count of the 0's that appear before the decimal point int i = 0; while ( i < maskBuffer.length() ){ boolean removeChar = false; switch ( maskBuffer.charAt(i) ){ case '_': if ( foundDecimal ){ maskBuffer.setCharAt( i, '0' ); }else{ maskBuffer.setCharAt( i, '#' ); } break; case '9': if ( foundDecimal || foundZero ){ maskBuffer.setCharAt( i, '0' ); if ( !foundDecimal ) zeroCount++; }else{ maskBuffer.setCharAt( i, '#' ); } break; case '.': if ( foundDecimal ){ removeChar = true; }else{ foundDecimal = true; } break; case '0': foundZero = true; if ( !foundDecimal ) zeroCount++; break; case '(': case ')': useBrackets = true; removeChar = true; break; case '+': usePlus = true; removeChar = true; break; case '-': useMinus = true; removeChar = true; break; case ',': useComma = true; removeChar = true; maskLength++; // don't want the mask length to be effected break; case 'L': justification = LEFT; removeChar = true; break; case 'C': justification = CENTER; removeChar = true; break; case '$': useDollar = true; removeChar = true; break; case '^': removeChar = true; break; default: throwException( _session, parameters.get(0).getString() + " is an invalid mask. Valid chars are '_', '9', '.', '0', '(', ')', '+', '-', ',', 'L', 'C', '$' and '^'." ); } if ( removeChar ){ maskBuffer = maskBuffer.deleteCharAt( i ); maskLength--; continue; } i++; } mask = new String( maskBuffer ); java.text.DecimalFormat DF = null; try{ DF = new java.text.DecimalFormat(mask,_dfs); }catch( IllegalArgumentException e ){ throwException( _session, parameters.get(0).getString() + " is an invalid mask." ); } if ( (int) val == 0 ){ DF.setMinimumIntegerDigits( zeroCount>0 ? zeroCount : 1 ); } if ( useComma ){ DF.setGroupingUsed(true); DF.setGroupingSize( 3 ); } // note absolute number is passed to format(). '-' is added in later if needed String formattedNum = null; formattedNum = DF.format( Math.abs( val ) ); StringBuilder formattedNumBuffer = new StringBuilder( formattedNum ); if ( symbolsFirst ){ int widthBefore = formattedNumBuffer.length(); applySymbolics( formattedNumBuffer, val, usePlus, useMinus, useDollar, useBrackets, _dfs, _locale ); int offset = formattedNumBuffer.length() - widthBefore; applyJustification( formattedNumBuffer, justification, maskLength+offset ); }else{ applyJustification( formattedNumBuffer, justification, maskLength ); applySymbolics( formattedNumBuffer, val, usePlus, useMinus, useDollar, useBrackets , _dfs, _locale ); } formattedNum = formattedNumBuffer.toString(); return new cfStringData( formattedNum ); } private void applyJustification( StringBuilder _buffer, int _just, int _width ){ if ( _buffer.length() < _width ){ int padding = _width - _buffer.length(); // apply justification if ( _just == CENTER ){ centerJustify( _buffer, padding ); }else if ( _just == LEFT ){ leftJustify( _buffer, padding ); }else{ rightJustify( _buffer, padding ); } } }// applyJustification // handles all the addition of +, -, $ and brackets to the number in the StringBuffer private static void applySymbolics( StringBuilder _buffer, double _no, boolean _usePlus, boolean _useMinus, boolean _useDollar, boolean _useBrackets, DecimalFormatSymbols _dfs, Locale _locale ){ if ( _useBrackets && _no < 0.0 ){ _buffer.insert( 0, '(' ); _buffer.append( ')' ); } if ( _usePlus ){ _buffer.insert( 0, ( _no >= 0 ? '+' : _dfs.getMinusSign() ) ); } if ( _no < 0 && !_useBrackets && !_usePlus ){ _buffer.insert( 0, _dfs.getMinusSign() ); }else if ( _useMinus ){ _buffer.insert( 0, ' ' ); } if ( _useDollar ){ _buffer.insert( 0, new DecimalFormatSymbols(_locale).getCurrencySymbol() ); } } // pads out the stringbuffer so the contents are center justified with // _padding no of spaces private void centerJustify( StringBuilder _src, int _padding ){ int padSplit = _padding / 2 + 1; rightJustify( _src, padSplit ); leftJustify( _src, padSplit ); } // pads out the stringbuffer so the contents are right justified with // _padding no of spaces private static void rightJustify( StringBuilder _src, int _padding ){ for ( int x=0; x < _padding; x++ ) _src.insert( 0, ' ' ); } // pads out the stringbuffer so the contents are left justified with // _padding no of spaces private static void leftJustify( StringBuilder _src, int _padding ){ for ( int x=0; x < _padding; x++ ) _src.append( ' ' ); } }