001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019/** 020 * Implements the CFML Function numberformat 021 */ 022package lucee.runtime.functions.displayFormatting; 023 024import java.util.Locale; 025 026import lucee.runtime.PageContext; 027import lucee.runtime.exp.ExpressionException; 028import lucee.runtime.exp.FunctionException; 029import lucee.runtime.exp.PageException; 030import lucee.runtime.ext.function.Function; 031import lucee.runtime.op.Caster; 032import lucee.runtime.op.Decision; 033import lucee.runtime.util.InvalidMaskException; 034 035/** 036 * Formats a Number by given pattern 037 */ 038public final class NumberFormat implements Function { 039 040 /** 041 * @param pc 042 * @param object 043 * @return formated number 044 * @throws ExpressionException 045 */ 046 public static String call(PageContext pc, Object object) throws PageException { 047 return new lucee.runtime.util.NumberFormat().format(Locale.US,toNumber(pc,object)); 048 } 049 050 /** 051 * @param pc 052 * @param object 053 * @param mask 054 * @return formated number 055 * @throws ExpressionException 056 */ 057 public static String call(PageContext pc , Object object, String mask) throws PageException { 058 if(mask==null) return call(pc, object); 059 if(mask.equalsIgnoreCase("roman")) { 060 return intToRoman(pc,(int)toNumber(pc,object)); 061 } 062 else if(mask.equalsIgnoreCase("hex")) { 063 return Integer.toHexString((int)toNumber(pc,object)); 064 } 065 else if(mask.equalsIgnoreCase(",")) { 066 return call(pc, object); 067 } 068 069 try { 070 return new lucee.runtime.util.NumberFormat().format(Locale.US,toNumber(pc,object),mask); 071 } 072 catch (InvalidMaskException e) { 073 throw new FunctionException(pc,"numberFormat",2,"mask",e.getMessage()); 074 } 075 } 076 077 public static double toNumber(PageContext pc, Object object) throws PageException { 078 double d=Caster.toDoubleValue(object,true,Double.NaN); 079 if(Decision.isValid(d)) return d; 080 081 String str=Caster.toString(object); 082 if(str.length()==0) return 0; 083 throw new FunctionException(pc,"numberFormat",1,"number","can't cast value ["+str+"] to a number"); 084 } 085 086 087 private static String intToRoman(PageContext pc, int value) throws FunctionException { 088 if(value == 0) 089 throw new FunctionException(pc,"numberFormat",1,"number","a roman value can't be 0"); 090 if(value < 0) 091 throw new FunctionException(pc,"numberFormat",1,"number","a roman value can't be less than 0"); 092 if(value > 3999) 093 throw new FunctionException(pc,"numberFormat",1,"number","a roman value can't be greater than 3999"); 094 095 StringBuilder roman = new StringBuilder(); 096 097 098 099 while (value / 1000 >= 1) { 100 roman.append('M'); 101 value = value - 1000; 102 } 103 if (value / 900 >= 1) { 104 roman.append("CM"); 105 value = value - 900; 106 } 107 if (value / 500 >= 1) { 108 roman.append("D"); 109 value = value - 500; 110 } 111 if (value / 400 >= 1) { 112 roman.append("CD"); 113 value = value - 400; 114 } 115 while (value / 100 >= 1) { 116 roman.append("C"); 117 value = value - 100; 118 } 119 if (value / 90 >= 1) { 120 roman.append("XC"); 121 value = value - 90; 122 } 123 if (value / 50 >= 1) { 124 roman.append("L"); 125 value = value - 50; 126 } 127 if (value / 40 >= 1) { 128 roman.append("XL"); 129 value = value - 40; 130 } 131 while (value / 10 >= 1) { 132 roman.append("X"); 133 value = value - 10; 134 } 135 if (value / 9 >= 1) { 136 roman.append("IX"); 137 value = value - 9; 138 } 139 if (value / 5 >= 1) { 140 roman.append("V"); 141 value = value - 5; 142 } 143 if (value / 4 >= 1) { 144 roman.append("IV"); 145 value = value - 4; 146 } 147 while (value >= 1) { 148 roman.append("I"); 149 value = value - 1; 150 } 151 return roman.toString(); 152 } 153 154}