/* ==================================================================== | |
Licensed to the Apache Software Foundation (ASF) under one or more | |
contributor license agreements. See the NOTICE file distributed with | |
this work for additional information regarding copyright ownership. | |
The ASF licenses this file to You under the Apache License, Version 2.0 | |
(the "License"); you may not use this file except in compliance with | |
the License. You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, software | |
distributed under the License is distributed on an "AS IS" BASIS, | |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License. | |
==================================================================== */ | |
package org.apache.poi.ss.formula.functions; | |
import org.apache.poi.ss.formula.eval.ErrorEval; | |
import org.apache.poi.ss.formula.eval.EvaluationException; | |
import org.apache.poi.ss.formula.eval.OperandResolver; | |
import org.apache.poi.ss.formula.eval.StringEval; | |
import org.apache.poi.ss.formula.eval.ValueEval; | |
/** | |
* Implementation for Excel WeekNum() function.<p/> | |
* <p/> | |
* <b>Syntax</b>:<br/> <b>WeekNum </b>(<b>Serial_num</b>,<b>Return_type</b>)<br/> | |
* <p/> | |
* Returns a number that indicates where the week falls numerically within a year. | |
* <p/> | |
* <p/> | |
* Serial_num is a date within the week. Dates should be entered by using the DATE function, | |
* or as results of other formulas or functions. For example, use DATE(2008,5,23) | |
* for the 23rd day of May, 2008. Problems can occur if dates are entered as text. | |
* Return_type is a number that determines on which day the week begins. The default is 1. | |
* 1 Week begins on Sunday. Weekdays are numbered 1 through 7. | |
* 2 Week begins on Monday. Weekdays are numbered 1 through 7. | |
* | |
* @author cedric dot walter @ gmail dot com | |
*/ | |
public class Roman extends Fixed2ArgFunction { | |
//M (1000), CM (900), D (500), CD (400), C (100), XC (90), L (50), XL (40), X (10), IX (9), V (5), IV (4) and I (1). | |
public static final int[] VALUES = new int[]{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; | |
public static final String[] ROMAN = new String[] | |
{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; | |
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval numberVE, ValueEval formVE) { | |
int number = 0; | |
try { | |
ValueEval ve = OperandResolver.getSingleValue(numberVE, srcRowIndex, srcColumnIndex); | |
number = OperandResolver.coerceValueToInt(ve); | |
} catch (EvaluationException e) { | |
return ErrorEval.VALUE_INVALID; | |
} | |
if (number < 0) { | |
return ErrorEval.VALUE_INVALID; | |
} | |
if (number > 3999) { | |
return ErrorEval.VALUE_INVALID; | |
} | |
if (number == 0) { | |
return new StringEval(""); | |
} | |
int form = 0; | |
try { | |
ValueEval ve = OperandResolver.getSingleValue(formVE, srcRowIndex, srcColumnIndex); | |
form = OperandResolver.coerceValueToInt(ve); | |
} catch (EvaluationException e) { | |
return ErrorEval.NUM_ERROR; | |
} | |
if (form > 4 || form < 0) { | |
return ErrorEval.VALUE_INVALID; | |
} | |
String result = this.integerToRoman(number); | |
if (form == 0) { | |
return new StringEval(result); | |
} | |
return new StringEval(makeConcise(result, form)); | |
} | |
/** | |
* Classic conversion. | |
* | |
* @param number | |
*/ | |
private String integerToRoman(int number) { | |
StringBuilder result = new StringBuilder(); | |
for (int i = 0; i < 13; i++) { | |
while (number >= VALUES[i]) { | |
number -= VALUES[i]; | |
result.append(ROMAN[i]); | |
} | |
} | |
return result.toString(); | |
} | |
/** | |
* Use conversion rule to factor some parts and make them more concise | |
* | |
* @param result | |
* @param form | |
*/ | |
public String makeConcise(String result, int form) { | |
if (form > 0) { | |
result = result.replaceAll("XLV", "VL"); //45 | |
result = result.replaceAll("XCV", "VC"); //95 | |
result = result.replaceAll("CDL", "LD"); //450 | |
result = result.replaceAll("CML", "LM"); //950 | |
result = result.replaceAll("CMVC", "LMVL"); //995 | |
} | |
if (form == 1) { | |
result = result.replaceAll("CDXC", "LDXL"); //490 | |
result = result.replaceAll("CDVC", "LDVL"); //495 | |
result = result.replaceAll("CMXC", "LMXL"); //990 | |
result = result.replaceAll("XCIX", "VCIV"); //99 | |
result = result.replaceAll("XLIX", "VLIV"); //49 | |
} | |
if (form > 1) { | |
result = result.replaceAll("XLIX", "IL"); //49 | |
result = result.replaceAll("XCIX", "IC"); //99 | |
result = result.replaceAll("CDXC", "XD"); //490 | |
result = result.replaceAll("CDVC", "XDV"); //495 | |
result = result.replaceAll("CDIC", "XDIX"); //499 | |
result = result.replaceAll("LMVL", "XMV"); //995 | |
result = result.replaceAll("CMIC", "XMIX"); //999 | |
result = result.replaceAll("CMXC", "XM"); // 990 | |
} | |
if (form > 2) { | |
result = result.replaceAll("XDV", "VD"); //495 | |
result = result.replaceAll("XDIX", "VDIV"); //499 | |
result = result.replaceAll("XMV", "VM"); // 995 | |
result = result.replaceAll("XMIX", "VMIV"); //999 | |
} | |
if (form == 4) { | |
result = result.replaceAll("VDIV", "ID"); //499 | |
result = result.replaceAll("VMIV", "IM"); //999 | |
} | |
return result; | |
} | |
} |