Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

DataFormatter.java 39KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. 2012 - Alfresco Software, Ltd.
  15. Alfresco Software has modified source of this file
  16. The details of changes as svn diff can be found in svn at location root/projects/3rd-party/src
  17. ==================================================================== */
  18. package org.apache.poi.ss.usermodel;
  19. import java.math.RoundingMode;
  20. import java.text.DateFormatSymbols;
  21. import java.text.DecimalFormat;
  22. import java.text.DecimalFormatSymbols;
  23. import java.text.FieldPosition;
  24. import java.text.Format;
  25. import java.text.ParsePosition;
  26. import java.text.SimpleDateFormat;
  27. import java.util.ArrayList;
  28. import java.util.Date;
  29. import java.util.HashMap;
  30. import java.util.Iterator;
  31. import java.util.List;
  32. import java.util.Locale;
  33. import java.util.Map;
  34. import java.util.regex.Matcher;
  35. import java.util.regex.Pattern;
  36. /**
  37. * DataFormatter contains methods for formatting the value stored in an
  38. * Cell. This can be useful for reports and GUI presentations when you
  39. * need to display data exactly as it appears in Excel. Supported formats
  40. * include currency, SSN, percentages, decimals, dates, phone numbers, zip
  41. * codes, etc.
  42. * <p>
  43. * Internally, formats will be implemented using subclasses of {@link Format}
  44. * such as {@link DecimalFormat} and {@link SimpleDateFormat}. Therefore the
  45. * formats used by this class must obey the same pattern rules as these Format
  46. * subclasses. This means that only legal number pattern characters ("0", "#",
  47. * ".", "," etc.) may appear in number formats. Other characters can be
  48. * inserted <em>before</em> or <em> after</em> the number pattern to form a
  49. * prefix or suffix.
  50. * </p>
  51. * <p>
  52. * For example the Excel pattern <code>"$#,##0.00 "USD"_);($#,##0.00 "USD")"
  53. * </code> will be correctly formatted as "$1,000.00 USD" or "($1,000.00 USD)".
  54. * However the pattern <code>"00-00-00"</code> is incorrectly formatted by
  55. * DecimalFormat as "000000--". For Excel formats that are not compatible with
  56. * DecimalFormat, you can provide your own custom {@link Format} implementation
  57. * via <code>DataFormatter.addFormat(String,Format)</code>. The following
  58. * custom formats are already provided by this class:
  59. * </p>
  60. * <pre>
  61. * <ul><li>SSN "000-00-0000"</li>
  62. * <li>Phone Number "(###) ###-####"</li>
  63. * <li>Zip plus 4 "00000-0000"</li>
  64. * </ul>
  65. * </pre>
  66. * <p>
  67. * If the Excel format pattern cannot be parsed successfully, then a default
  68. * format will be used. The default number format will mimic the Excel General
  69. * format: "#" for whole numbers and "#.##########" for decimal numbers. You
  70. * can override the default format pattern with <code>
  71. * DataFormatter.setDefaultNumberFormat(Format)</code>. <b>Note:</b> the
  72. * default format will only be used when a Format cannot be created from the
  73. * cell's data format string.
  74. *
  75. * <p>
  76. * Note that by default formatted numeric values are trimmed.
  77. * Excel formats can contain spacers and padding and the default behavior is to strip them off.
  78. * </p>
  79. * <p>Example:</p>
  80. * <p>
  81. * Consider a numeric cell with a value <code>12.343</code> and format <code>"##.##_ "</code>.
  82. * The trailing underscore and space ("_ ") in the format adds a space to the end and Excel formats this cell as <code>"12.34 "</code>,
  83. * but <code>DataFormatter</code> trims the formatted value and returns <code>"12.34"</code>.
  84. * </p>
  85. * You can enable spaces by passing the <code>emulateCsv=true</code> flag in the <code>DateFormatter</code> cosntructor.
  86. * If set to true, then the output tries to conform to what you get when you take an xls or xlsx in Excel and Save As CSV file:
  87. * <ul>
  88. * <li>returned values are not trimmed</li>
  89. * <li>Invalid dates are formatted as 255 pound signs ("#")</li>
  90. * <li>simulate Excel's handling of a format string of all # when the value is 0.
  91. * Excel will output "", <code>DataFormatter</code> will output "0".
  92. * </ul>
  93. */
  94. public class DataFormatter {
  95. private static final String defaultFractionWholePartFormat = "#";
  96. private static final String defaultFractionFractionPartFormat = "#/##";
  97. /** Pattern to find a number format: "0" or "#" */
  98. private static final Pattern numPattern = Pattern.compile("[0#]+");
  99. /** Pattern to find days of week as text "ddd...." */
  100. private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE);
  101. /** Pattern to find "AM/PM" marker */
  102. private static final Pattern amPmPattern = Pattern.compile("((A|P)[M/P]*)", Pattern.CASE_INSENSITIVE);
  103. /**
  104. * A regex to find locale patterns like [$$-1009] and [$?-452].
  105. * Note that we don't currently process these into locales
  106. */
  107. private static final Pattern localePatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])");
  108. /**
  109. * A regex to match the colour formattings rules.
  110. * Allowed colours are: Black, Blue, Cyan, Green,
  111. * Magenta, Red, White, Yellow, "Color n" (1<=n<=56)
  112. */
  113. private static final Pattern colorPattern =
  114. Pattern.compile("(\\[BLACK\\])|(\\[BLUE\\])|(\\[CYAN\\])|(\\[GREEN\\])|" +
  115. "(\\[MAGENTA\\])|(\\[RED\\])|(\\[WHITE\\])|(\\[YELLOW\\])|" +
  116. "(\\[COLOR\\s*\\d\\])|(\\[COLOR\\s*[0-5]\\d\\])", Pattern.CASE_INSENSITIVE);
  117. /**
  118. * A regex to identify a fraction pattern.
  119. * This requires that replaceAll("\\?", "#") has already been called
  120. */
  121. private static final Pattern fractionPattern = Pattern.compile("(?:([#\\d]+)\\s+)?(#+)\\s*\\/\\s*([#\\d]+)");
  122. /**
  123. * A regex to strip junk out of fraction formats
  124. */
  125. private static final Pattern fractionStripper = Pattern.compile("(\"[^\"]*\")|([^ \\?#\\d\\/]+)");
  126. /**
  127. * Cells formatted with a date or time format and which contain invalid date or time values
  128. * show 255 pound signs ("#").
  129. */
  130. private static final String invalidDateTimeString;
  131. static {
  132. StringBuilder buf = new StringBuilder();
  133. for(int i = 0; i < 255; i++) buf.append('#');
  134. invalidDateTimeString = buf.toString();
  135. }
  136. /**
  137. * The decimal symbols of the locale used for formatting values.
  138. */
  139. private final DecimalFormatSymbols decimalSymbols;
  140. /**
  141. * The date symbols of the locale used for formatting values.
  142. */
  143. private final DateFormatSymbols dateSymbols;
  144. /** <em>General</em> format for whole numbers. */
  145. private final Format generalWholeNumFormat;
  146. /** <em>General</em> format for decimal numbers. */
  147. private final Format generalDecimalNumFormat;
  148. /** A default format to use when a number pattern cannot be parsed. */
  149. private Format defaultNumFormat;
  150. /**
  151. * A map to cache formats.
  152. * Map<String,Format> formats
  153. */
  154. private final Map<String,Format> formats;
  155. private boolean emulateCsv = false;
  156. /**
  157. * Creates a formatter using the {@link Locale#getDefault() default locale}.
  158. */
  159. public DataFormatter() {
  160. this(false);
  161. }
  162. /**
  163. * Creates a formatter using the {@link Locale#getDefault() default locale}.
  164. *
  165. * @param emulateCsv whether to emulate CSV output.
  166. */
  167. public DataFormatter(boolean emulateCsv) {
  168. this(Locale.getDefault());
  169. this.emulateCsv = emulateCsv;
  170. }
  171. /**
  172. * Creates a formatter using the given locale.
  173. *
  174. * @param emulateCsv whether to emulate CSV output.
  175. */
  176. public DataFormatter(Locale locale, boolean emulateCsv) {
  177. this(locale);
  178. this.emulateCsv = emulateCsv;
  179. }
  180. /**
  181. * Creates a formatter using the given locale.
  182. */
  183. public DataFormatter(Locale locale) {
  184. dateSymbols = new DateFormatSymbols(locale);
  185. decimalSymbols = new DecimalFormatSymbols(locale);
  186. generalWholeNumFormat = new DecimalFormat("#", decimalSymbols);
  187. generalDecimalNumFormat = new DecimalFormat("#.##########", decimalSymbols);
  188. formats = new HashMap<String,Format>();
  189. // init built-in formats
  190. Format zipFormat = ZipPlusFourFormat.instance;
  191. addFormat("00000\\-0000", zipFormat);
  192. addFormat("00000-0000", zipFormat);
  193. Format phoneFormat = PhoneFormat.instance;
  194. // allow for format string variations
  195. addFormat("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
  196. addFormat("[<=9999999]###-####;(###) ###-####", phoneFormat);
  197. addFormat("###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
  198. addFormat("###-####;(###) ###-####", phoneFormat);
  199. Format ssnFormat = SSNFormat.instance;
  200. addFormat("000\\-00\\-0000", ssnFormat);
  201. addFormat("000-00-0000", ssnFormat);
  202. }
  203. /**
  204. * Return a Format for the given cell if one exists, otherwise try to
  205. * create one. This method will return <code>null</code> if the any of the
  206. * following is true:
  207. * <ul>
  208. * <li>the cell's style is null</li>
  209. * <li>the style's data format string is null or empty</li>
  210. * <li>the format string cannot be recognized as either a number or date</li>
  211. * </ul>
  212. *
  213. * @param cell The cell to retrieve a Format for
  214. * @return A Format for the format String
  215. */
  216. private Format getFormat(Cell cell) {
  217. if ( cell.getCellStyle() == null) {
  218. return null;
  219. }
  220. int formatIndex = cell.getCellStyle().getDataFormat();
  221. String formatStr = cell.getCellStyle().getDataFormatString();
  222. if(formatStr == null || formatStr.trim().length() == 0) {
  223. return null;
  224. }
  225. return getFormat(cell.getNumericCellValue(), formatIndex, formatStr);
  226. }
  227. private Format getFormat(double cellValue, int formatIndex, String formatStrIn) {
  228. // // Might be better to separate out the n p and z formats, falling back to p when n and z are not set.
  229. // // That however would require other code to be re factored.
  230. // String[] formatBits = formatStrIn.split(";");
  231. // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2;
  232. // String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0];
  233. String formatStr = formatStrIn;
  234. // Excel supports positive/negative/zero, but java
  235. // doesn't, so we need to do it specially
  236. final int firstAt = formatStr.indexOf(';');
  237. final int lastAt = formatStr.lastIndexOf(';');
  238. // p and p;n are ok by default. p;n;z and p;n;z;s need to be fixed.
  239. if (firstAt != -1 && firstAt != lastAt) {
  240. final int secondAt = formatStr.indexOf(';', firstAt + 1);
  241. if (secondAt == lastAt) { // p;n;z
  242. if (cellValue == 0.0) {
  243. formatStr = formatStr.substring(lastAt + 1);
  244. } else {
  245. formatStr = formatStr.substring(0, lastAt);
  246. }
  247. } else {
  248. if (cellValue == 0.0) { // p;n;z;s
  249. formatStr = formatStr.substring(secondAt + 1, lastAt);
  250. } else {
  251. formatStr = formatStr.substring(0, secondAt);
  252. }
  253. }
  254. }
  255. // Excel's # with value 0 will output empty where Java will output 0. This hack removes the # from the format.
  256. if (emulateCsv && cellValue == 0.0 && formatStr.contains("#") && !formatStr.contains("0")) {
  257. formatStr = formatStr.replaceAll("#", "");
  258. }
  259. // See if we already have it cached
  260. Format format = formats.get(formatStr);
  261. if (format != null) {
  262. return format;
  263. }
  264. // Is it one of the special built in types, General or @?
  265. if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
  266. if (isWholeNumber(cellValue)) {
  267. return generalWholeNumFormat;
  268. }
  269. return generalDecimalNumFormat;
  270. }
  271. // Build a formatter, and cache it
  272. format = createFormat(cellValue, formatIndex, formatStr);
  273. formats.put(formatStr, format);
  274. return format;
  275. }
  276. /**
  277. * Create and return a Format based on the format string from a cell's
  278. * style. If the pattern cannot be parsed, return a default pattern.
  279. *
  280. * @param cell The Excel cell
  281. * @return A Format representing the excel format. May return null.
  282. */
  283. public Format createFormat(Cell cell) {
  284. int formatIndex = cell.getCellStyle().getDataFormat();
  285. String formatStr = cell.getCellStyle().getDataFormatString();
  286. return createFormat(cell.getNumericCellValue(), formatIndex, formatStr);
  287. }
  288. private Format createFormat(double cellValue, int formatIndex, String sFormat) {
  289. String formatStr = sFormat;
  290. // Remove colour formatting if present
  291. Matcher colourM = colorPattern.matcher(formatStr);
  292. while(colourM.find()) {
  293. String colour = colourM.group();
  294. // Paranoid replacement...
  295. int at = formatStr.indexOf(colour);
  296. if(at == -1) break;
  297. String nFormatStr = formatStr.substring(0,at) +
  298. formatStr.substring(at+colour.length());
  299. if(nFormatStr.equals(formatStr)) break;
  300. // Try again in case there's multiple
  301. formatStr = nFormatStr;
  302. colourM = colorPattern.matcher(formatStr);
  303. }
  304. // Strip off the locale information, we use an instance-wide locale for everything
  305. Matcher m = localePatternGroup.matcher(formatStr);
  306. while(m.find()) {
  307. String match = m.group();
  308. String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-'));
  309. if (symbol.indexOf('$') > -1) {
  310. StringBuffer sb = new StringBuffer();
  311. sb.append(symbol.substring(0, symbol.indexOf('$')));
  312. sb.append('\\');
  313. sb.append(symbol.substring(symbol.indexOf('$'), symbol.length()));
  314. symbol = sb.toString();
  315. }
  316. formatStr = m.replaceAll(symbol);
  317. m = localePatternGroup.matcher(formatStr);
  318. }
  319. // Check for special cases
  320. if(formatStr == null || formatStr.trim().length() == 0) {
  321. return getDefaultFormat(cellValue);
  322. }
  323. if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
  324. if (isWholeNumber(cellValue)) {
  325. return generalWholeNumFormat;
  326. }
  327. return generalDecimalNumFormat;
  328. }
  329. if(DateUtil.isADateFormat(formatIndex,formatStr) &&
  330. DateUtil.isValidExcelDate(cellValue)) {
  331. return createDateFormat(formatStr, cellValue);
  332. }
  333. // Excel supports fractions in format strings, which Java doesn't
  334. if (formatStr.indexOf("#/") >= 0 || formatStr.indexOf("?/") >= 0) {
  335. String[] chunks = formatStr.split(";");
  336. for (int i = 0; i < chunks.length; i++){
  337. String chunk = chunks[i].replaceAll("\\?", "#");
  338. Matcher matcher = fractionStripper.matcher(chunk);
  339. chunk = matcher.replaceAll(" ");
  340. chunk = chunk.replaceAll(" +", " ");
  341. Matcher fractionMatcher = fractionPattern.matcher(chunk);
  342. //take the first match
  343. if (fractionMatcher.find()){
  344. String wholePart = (fractionMatcher.group(1) == null) ? "" : defaultFractionWholePartFormat;
  345. return new FractionFormat(wholePart, fractionMatcher.group(3));
  346. }
  347. }
  348. // Strip custom text in quotes and escaped characters for now as it can cause performance problems in fractions.
  349. //String strippedFormatStr = formatStr.replaceAll("\\\\ ", " ").replaceAll("\\\\.", "").replaceAll("\"[^\"]*\"", " ").replaceAll("\\?", "#");
  350. //System.out.println("formatStr: "+strippedFormatStr);
  351. return new FractionFormat(defaultFractionWholePartFormat, defaultFractionFractionPartFormat);
  352. }
  353. if (numPattern.matcher(formatStr).find()) {
  354. return createNumberFormat(formatStr, cellValue);
  355. }
  356. if (emulateCsv) {
  357. return new ConstantStringFormat(cleanFormatForNumber(formatStr));
  358. }
  359. // TODO - when does this occur?
  360. return null;
  361. }
  362. private Format createDateFormat(String pFormatStr, double cellValue) {
  363. String formatStr = pFormatStr;
  364. formatStr = formatStr.replaceAll("\\\\-","-");
  365. formatStr = formatStr.replaceAll("\\\\,",",");
  366. formatStr = formatStr.replaceAll("\\\\\\.","."); // . is a special regexp char
  367. formatStr = formatStr.replaceAll("\\\\ "," ");
  368. formatStr = formatStr.replaceAll("\\\\/","/"); // weird: m\\/d\\/yyyy
  369. formatStr = formatStr.replaceAll(";@", "");
  370. formatStr = formatStr.replaceAll("\"/\"", "/"); // "/" is escaped for no reason in: mm"/"dd"/"yyyy
  371. formatStr = formatStr.replace("\"\"", "'"); // replace Excel quoting with Java style quoting
  372. formatStr = formatStr.replaceAll("\\\\T","'T'"); // Quote the T is iso8601 style dates
  373. boolean hasAmPm = false;
  374. Matcher amPmMatcher = amPmPattern.matcher(formatStr);
  375. while (amPmMatcher.find()) {
  376. formatStr = amPmMatcher.replaceAll("@");
  377. hasAmPm = true;
  378. amPmMatcher = amPmPattern.matcher(formatStr);
  379. }
  380. formatStr = formatStr.replaceAll("@", "a");
  381. Matcher dateMatcher = daysAsText.matcher(formatStr);
  382. if (dateMatcher.find()) {
  383. String match = dateMatcher.group(0);
  384. formatStr = dateMatcher.replaceAll(match.toUpperCase().replaceAll("D", "E"));
  385. }
  386. // Convert excel date format to SimpleDateFormat.
  387. // Excel uses lower and upper case 'm' for both minutes and months.
  388. // From Excel help:
  389. /*
  390. The "m" or "mm" code must appear immediately after the "h" or"hh"
  391. code or immediately before the "ss" code; otherwise, Microsoft
  392. Excel displays the month instead of minutes."
  393. */
  394. StringBuffer sb = new StringBuffer();
  395. char[] chars = formatStr.toCharArray();
  396. boolean mIsMonth = true;
  397. List<Integer> ms = new ArrayList<Integer>();
  398. boolean isElapsed = false;
  399. for(int j=0; j<chars.length; j++) {
  400. char c = chars[j];
  401. if (c == '\'') {
  402. sb.append(c);
  403. j++;
  404. // skip until the next quote
  405. while(j<chars.length) {
  406. c = chars[j];
  407. sb.append(c);
  408. if(c == '\'') {
  409. break;
  410. }
  411. j++;
  412. }
  413. }
  414. else if (c == '[' && !isElapsed) {
  415. isElapsed = true;
  416. mIsMonth = false;
  417. sb.append(c);
  418. }
  419. else if (c == ']' && isElapsed) {
  420. isElapsed = false;
  421. sb.append(c);
  422. }
  423. else if (isElapsed) {
  424. if (c == 'h' || c == 'H') {
  425. sb.append('H');
  426. }
  427. else if (c == 'm' || c == 'M') {
  428. sb.append('m');
  429. }
  430. else if (c == 's' || c == 'S') {
  431. sb.append('s');
  432. }
  433. else {
  434. sb.append(c);
  435. }
  436. }
  437. else if (c == 'h' || c == 'H') {
  438. mIsMonth = false;
  439. if (hasAmPm) {
  440. sb.append('h');
  441. } else {
  442. sb.append('H');
  443. }
  444. }
  445. else if (c == 'm' || c == 'M') {
  446. if(mIsMonth) {
  447. sb.append('M');
  448. ms.add(
  449. Integer.valueOf(sb.length() -1)
  450. );
  451. } else {
  452. sb.append('m');
  453. }
  454. }
  455. else if (c == 's' || c == 'S') {
  456. sb.append('s');
  457. // if 'M' precedes 's' it should be minutes ('m')
  458. for (int i = 0; i < ms.size(); i++) {
  459. int index = ms.get(i).intValue();
  460. if (sb.charAt(index) == 'M') {
  461. sb.replace(index, index+1, "m");
  462. }
  463. }
  464. mIsMonth = true;
  465. ms.clear();
  466. }
  467. else if (Character.isLetter(c)) {
  468. mIsMonth = true;
  469. ms.clear();
  470. if (c == 'y' || c == 'Y') {
  471. sb.append('y');
  472. }
  473. else if (c == 'd' || c == 'D') {
  474. sb.append('d');
  475. }
  476. else {
  477. sb.append(c);
  478. }
  479. }
  480. else {
  481. sb.append(c);
  482. }
  483. }
  484. formatStr = sb.toString();
  485. try {
  486. return new ExcelStyleDateFormatter(formatStr, dateSymbols);
  487. } catch(IllegalArgumentException iae) {
  488. // the pattern could not be parsed correctly,
  489. // so fall back to the default number format
  490. return getDefaultFormat(cellValue);
  491. }
  492. }
  493. private String cleanFormatForNumber(String formatStr) {
  494. StringBuffer sb = new StringBuffer(formatStr);
  495. if (emulateCsv) {
  496. // Requested spacers with "_" are replaced by a single space.
  497. // Full-column-width padding "*" are removed.
  498. // Not processing fractions at this time. Replace ? with space.
  499. // This matches CSV output.
  500. for (int i = 0; i < sb.length(); i++) {
  501. char c = sb.charAt(i);
  502. if (c == '_' || c == '*' || c == '?') {
  503. if (i > 0 && sb.charAt((i - 1)) == '\\') {
  504. // It's escaped, don't worry
  505. continue;
  506. }
  507. if (c == '?') {
  508. sb.setCharAt(i, ' ');
  509. } else if (i < sb.length() - 1) {
  510. // Remove the character we're supposed
  511. // to match the space of / pad to the
  512. // column width with
  513. if (c == '_') {
  514. sb.setCharAt(i + 1, ' ');
  515. } else {
  516. sb.deleteCharAt(i + 1);
  517. }
  518. // Remove the character too
  519. sb.deleteCharAt(i);
  520. i--;
  521. }
  522. }
  523. }
  524. } else {
  525. // If they requested spacers, with "_",
  526. // remove those as we don't do spacing
  527. // If they requested full-column-width
  528. // padding, with "*", remove those too
  529. for (int i = 0; i < sb.length(); i++) {
  530. char c = sb.charAt(i);
  531. if (c == '_' || c == '*') {
  532. if (i > 0 && sb.charAt((i - 1)) == '\\') {
  533. // It's escaped, don't worry
  534. continue;
  535. }
  536. if (i < sb.length() - 1) {
  537. // Remove the character we're supposed
  538. // to match the space of / pad to the
  539. // column width with
  540. sb.deleteCharAt(i + 1);
  541. }
  542. // Remove the _ too
  543. sb.deleteCharAt(i);
  544. i--;
  545. }
  546. }
  547. }
  548. // Now, handle the other aspects like
  549. // quoting and scientific notation
  550. for(int i = 0; i < sb.length(); i++) {
  551. char c = sb.charAt(i);
  552. // remove quotes and back slashes
  553. if (c == '\\' || c == '"') {
  554. sb.deleteCharAt(i);
  555. i--;
  556. // for scientific/engineering notation
  557. } else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') {
  558. sb.deleteCharAt(i);
  559. i--;
  560. }
  561. }
  562. return sb.toString();
  563. }
  564. private Format createNumberFormat(String formatStr, double cellValue) {
  565. final String format = cleanFormatForNumber(formatStr);
  566. try {
  567. DecimalFormat df = new DecimalFormat(format, decimalSymbols);
  568. setExcelStyleRoundingMode(df);
  569. return df;
  570. } catch(IllegalArgumentException iae) {
  571. // the pattern could not be parsed correctly,
  572. // so fall back to the default number format
  573. return getDefaultFormat(cellValue);
  574. }
  575. }
  576. /**
  577. * Return true if the double value represents a whole number
  578. * @param d the double value to check
  579. * @return <code>true</code> if d is a whole number
  580. */
  581. private static boolean isWholeNumber(double d) {
  582. return d == Math.floor(d);
  583. }
  584. /**
  585. * Returns a default format for a cell.
  586. * @param cell The cell
  587. * @return a default format
  588. */
  589. public Format getDefaultFormat(Cell cell) {
  590. return getDefaultFormat(cell.getNumericCellValue());
  591. }
  592. private Format getDefaultFormat(double cellValue) {
  593. // for numeric cells try user supplied default
  594. if (defaultNumFormat != null) {
  595. return defaultNumFormat;
  596. // otherwise use general format
  597. }
  598. if (isWholeNumber(cellValue)){
  599. return generalWholeNumFormat;
  600. }
  601. return generalDecimalNumFormat;
  602. }
  603. /**
  604. * Performs Excel-style date formatting, using the
  605. * supplied Date and format
  606. */
  607. private String performDateFormatting(Date d, Format dateFormat) {
  608. if(dateFormat != null) {
  609. return dateFormat.format(d);
  610. }
  611. return d.toString();
  612. }
  613. /**
  614. * Returns the formatted value of an Excel date as a <tt>String</tt> based
  615. * on the cell's <code>DataFormat</code>. i.e. "Thursday, January 02, 2003"
  616. * , "01/02/2003" , "02-Jan" , etc.
  617. *
  618. * @param cell The cell
  619. * @return a formatted date string
  620. */
  621. private String getFormattedDateString(Cell cell) {
  622. Format dateFormat = getFormat(cell);
  623. if(dateFormat instanceof ExcelStyleDateFormatter) {
  624. // Hint about the raw excel value
  625. ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(
  626. cell.getNumericCellValue()
  627. );
  628. }
  629. Date d = cell.getDateCellValue();
  630. return performDateFormatting(d, dateFormat);
  631. }
  632. /**
  633. * Returns the formatted value of an Excel number as a <tt>String</tt>
  634. * based on the cell's <code>DataFormat</code>. Supported formats include
  635. * currency, percents, decimals, phone number, SSN, etc.:
  636. * "61.54%", "$100.00", "(800) 555-1234".
  637. *
  638. * @param cell The cell
  639. * @return a formatted number string
  640. */
  641. private String getFormattedNumberString(Cell cell) {
  642. Format numberFormat = getFormat(cell);
  643. double d = cell.getNumericCellValue();
  644. if (numberFormat == null) {
  645. return String.valueOf(d);
  646. }
  647. return numberFormat.format(new Double(d));
  648. }
  649. /**
  650. * Formats the given raw cell value, based on the supplied
  651. * format index and string, according to excel style rules.
  652. * @see #formatCellValue(Cell)
  653. */
  654. public String formatRawCellContents(double value, int formatIndex, String formatString) {
  655. return formatRawCellContents(value, formatIndex, formatString, false);
  656. }
  657. /**
  658. * Formats the given raw cell value, based on the supplied
  659. * format index and string, according to excel style rules.
  660. * @see #formatCellValue(Cell)
  661. */
  662. public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) {
  663. // Is it a date?
  664. if(DateUtil.isADateFormat(formatIndex,formatString)) {
  665. if(DateUtil.isValidExcelDate(value)) {
  666. Format dateFormat = getFormat(value, formatIndex, formatString);
  667. if(dateFormat instanceof ExcelStyleDateFormatter) {
  668. // Hint about the raw excel value
  669. ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(value);
  670. }
  671. Date d = DateUtil.getJavaDate(value, use1904Windowing);
  672. return performDateFormatting(d, dateFormat);
  673. }
  674. // RK: Invalid dates are 255 #s.
  675. if (emulateCsv) {
  676. return invalidDateTimeString;
  677. }
  678. }
  679. // else Number
  680. Format numberFormat = getFormat(value, formatIndex, formatString);
  681. if (numberFormat == null) {
  682. return String.valueOf(value);
  683. }
  684. // RK: This hack handles scientific notation by adding the missing + back.
  685. String result = numberFormat.format(new Double(value));
  686. if (result.contains("E") && !result.contains("E-")) {
  687. result = result.replaceFirst("E", "E+");
  688. }
  689. return result;
  690. }
  691. /**
  692. * <p>
  693. * Returns the formatted value of a cell as a <tt>String</tt> regardless
  694. * of the cell type. If the Excel format pattern cannot be parsed then the
  695. * cell value will be formatted using a default format.
  696. * </p>
  697. * <p>When passed a null or blank cell, this method will return an empty
  698. * String (""). Formulas in formula type cells will not be evaluated.
  699. * </p>
  700. *
  701. * @param cell The cell
  702. * @return the formatted cell value as a String
  703. */
  704. public String formatCellValue(Cell cell) {
  705. return formatCellValue(cell, null);
  706. }
  707. /**
  708. * <p>
  709. * Returns the formatted value of a cell as a <tt>String</tt> regardless
  710. * of the cell type. If the Excel format pattern cannot be parsed then the
  711. * cell value will be formatted using a default format.
  712. * </p>
  713. * <p>When passed a null or blank cell, this method will return an empty
  714. * String (""). Formula cells will be evaluated using the given
  715. * {@link FormulaEvaluator} if the evaluator is non-null. If the
  716. * evaluator is null, then the formula String will be returned. The caller
  717. * is responsible for setting the currentRow on the evaluator
  718. *</p>
  719. *
  720. * @param cell The cell (can be null)
  721. * @param evaluator The FormulaEvaluator (can be null)
  722. * @return a string value of the cell
  723. */
  724. public String formatCellValue(Cell cell, FormulaEvaluator evaluator) {
  725. if (cell == null) {
  726. return "";
  727. }
  728. int cellType = cell.getCellType();
  729. if (cellType == Cell.CELL_TYPE_FORMULA) {
  730. if (evaluator == null) {
  731. return cell.getCellFormula();
  732. }
  733. cellType = evaluator.evaluateFormulaCell(cell);
  734. }
  735. switch (cellType) {
  736. case Cell.CELL_TYPE_NUMERIC :
  737. if (DateUtil.isCellDateFormatted(cell)) {
  738. return getFormattedDateString(cell);
  739. }
  740. return getFormattedNumberString(cell);
  741. case Cell.CELL_TYPE_STRING :
  742. return cell.getRichStringCellValue().getString();
  743. case Cell.CELL_TYPE_BOOLEAN :
  744. return String.valueOf(cell.getBooleanCellValue());
  745. case Cell.CELL_TYPE_BLANK :
  746. return "";
  747. case Cell.CELL_TYPE_ERROR:
  748. return FormulaError.forInt(cell.getErrorCellValue()).getString();
  749. }
  750. throw new RuntimeException("Unexpected celltype (" + cellType + ")");
  751. }
  752. /**
  753. * <p>
  754. * Sets a default number format to be used when the Excel format cannot be
  755. * parsed successfully. <b>Note:</b> This is a fall back for when an error
  756. * occurs while parsing an Excel number format pattern. This will not
  757. * affect cells with the <em>General</em> format.
  758. * </p>
  759. * <p>
  760. * The value that will be passed to the Format's format method (specified
  761. * by <code>java.text.Format#format</code>) will be a double value from a
  762. * numeric cell. Therefore the code in the format method should expect a
  763. * <code>Number</code> value.
  764. * </p>
  765. *
  766. * @param format A Format instance to be used as a default
  767. * @see java.text.Format#format
  768. */
  769. public void setDefaultNumberFormat(Format format) {
  770. Iterator<Map.Entry<String,Format>> itr = formats.entrySet().iterator();
  771. while(itr.hasNext()) {
  772. Map.Entry<String,Format> entry = itr.next();
  773. if (entry.getValue() == generalDecimalNumFormat
  774. || entry.getValue() == generalWholeNumFormat) {
  775. entry.setValue(format);
  776. }
  777. }
  778. defaultNumFormat = format;
  779. }
  780. /**
  781. * Adds a new format to the available formats.
  782. * <p>
  783. * The value that will be passed to the Format's format method (specified
  784. * by <code>java.text.Format#format</code>) will be a double value from a
  785. * numeric cell. Therefore the code in the format method should expect a
  786. * <code>Number</code> value.
  787. * </p>
  788. * @param excelFormatStr The data format string
  789. * @param format A Format instance
  790. */
  791. public void addFormat(String excelFormatStr, Format format) {
  792. formats.put(excelFormatStr, format);
  793. }
  794. // Some custom formats
  795. /**
  796. * @return a <tt>DecimalFormat</tt> with parseIntegerOnly set <code>true</code>
  797. */
  798. /* package */ static DecimalFormat createIntegerOnlyFormat(String fmt) {
  799. DecimalFormat result = new DecimalFormat(fmt);
  800. result.setParseIntegerOnly(true);
  801. return result;
  802. }
  803. /**
  804. * Enables excel style rounding mode (round half up) on the
  805. * Decimal Format given.
  806. */
  807. public static void setExcelStyleRoundingMode(DecimalFormat format) {
  808. setExcelStyleRoundingMode(format, RoundingMode.HALF_UP);
  809. }
  810. /**
  811. * Enables custom rounding mode on the given Decimal Format.
  812. * @param format DecimalFormat
  813. * @param roundingMode RoundingMode
  814. */
  815. public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) {
  816. format.setRoundingMode(roundingMode);
  817. }
  818. /**
  819. * Format class for Excel's SSN format. This class mimics Excel's built-in
  820. * SSN formatting.
  821. *
  822. * @author James May
  823. */
  824. @SuppressWarnings("serial")
  825. private static final class SSNFormat extends Format {
  826. public static final Format instance = new SSNFormat();
  827. private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
  828. private SSNFormat() {
  829. // enforce singleton
  830. }
  831. /** Format a number as an SSN */
  832. public static String format(Number num) {
  833. String result = df.format(num);
  834. StringBuffer sb = new StringBuffer();
  835. sb.append(result.substring(0, 3)).append('-');
  836. sb.append(result.substring(3, 5)).append('-');
  837. sb.append(result.substring(5, 9));
  838. return sb.toString();
  839. }
  840. @Override
  841. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  842. return toAppendTo.append(format((Number)obj));
  843. }
  844. @Override
  845. public Object parseObject(String source, ParsePosition pos) {
  846. return df.parseObject(source, pos);
  847. }
  848. }
  849. /**
  850. * Format class for Excel Zip + 4 format. This class mimics Excel's
  851. * built-in formatting for Zip + 4.
  852. * @author James May
  853. */
  854. @SuppressWarnings("serial")
  855. private static final class ZipPlusFourFormat extends Format {
  856. public static final Format instance = new ZipPlusFourFormat();
  857. private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
  858. private ZipPlusFourFormat() {
  859. // enforce singleton
  860. }
  861. /** Format a number as Zip + 4 */
  862. public static String format(Number num) {
  863. String result = df.format(num);
  864. StringBuffer sb = new StringBuffer();
  865. sb.append(result.substring(0, 5)).append('-');
  866. sb.append(result.substring(5, 9));
  867. return sb.toString();
  868. }
  869. @Override
  870. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  871. return toAppendTo.append(format((Number)obj));
  872. }
  873. @Override
  874. public Object parseObject(String source, ParsePosition pos) {
  875. return df.parseObject(source, pos);
  876. }
  877. }
  878. /**
  879. * Format class for Excel phone number format. This class mimics Excel's
  880. * built-in phone number formatting.
  881. * @author James May
  882. */
  883. @SuppressWarnings("serial")
  884. private static final class PhoneFormat extends Format {
  885. public static final Format instance = new PhoneFormat();
  886. private static final DecimalFormat df = createIntegerOnlyFormat("##########");
  887. private PhoneFormat() {
  888. // enforce singleton
  889. }
  890. /** Format a number as a phone number */
  891. public static String format(Number num) {
  892. String result = df.format(num);
  893. StringBuffer sb = new StringBuffer();
  894. String seg1, seg2, seg3;
  895. int len = result.length();
  896. if (len <= 4) {
  897. return result;
  898. }
  899. seg3 = result.substring(len - 4, len);
  900. seg2 = result.substring(Math.max(0, len - 7), len - 4);
  901. seg1 = result.substring(Math.max(0, len - 10), Math.max(0, len - 7));
  902. if(seg1 != null && seg1.trim().length() > 0) {
  903. sb.append('(').append(seg1).append(") ");
  904. }
  905. if(seg2 != null && seg2.trim().length() > 0) {
  906. sb.append(seg2).append('-');
  907. }
  908. sb.append(seg3);
  909. return sb.toString();
  910. }
  911. @Override
  912. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  913. return toAppendTo.append(format((Number)obj));
  914. }
  915. @Override
  916. public Object parseObject(String source, ParsePosition pos) {
  917. return df.parseObject(source, pos);
  918. }
  919. }
  920. /**
  921. * Format class that does nothing and always returns a constant string.
  922. *
  923. * This format is used to simulate Excel's handling of a format string
  924. * of all # when the value is 0. Excel will output "", Java will output "0".
  925. *
  926. * @see DataFormatter#createFormat(double, int, String)
  927. */
  928. @SuppressWarnings("serial")
  929. private static final class ConstantStringFormat extends Format {
  930. private static final DecimalFormat df = createIntegerOnlyFormat("##########");
  931. private final String str;
  932. public ConstantStringFormat(String s) {
  933. str = s;
  934. }
  935. @Override
  936. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  937. return toAppendTo.append(str);
  938. }
  939. @Override
  940. public Object parseObject(String source, ParsePosition pos) {
  941. return df.parseObject(source, pos);
  942. }
  943. }
  944. }