You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DataFormatter.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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. ==================================================================== */
  15. package org.apache.poi.ss.usermodel;
  16. import java.util.regex.Pattern;
  17. import java.util.regex.Matcher;
  18. import java.util.*;
  19. import java.text.*;
  20. /**
  21. * DataFormatter contains methods for formatting the value stored in an
  22. * Cell. This can be useful for reports and GUI presentations when you
  23. * need to display data exactly as it appears in Excel. Supported formats
  24. * include currency, SSN, percentages, decimals, dates, phone numbers, zip
  25. * codes, etc.
  26. * <p>
  27. * Internally, formats will be implemented using subclasses of {@link Format}
  28. * such as {@link DecimalFormat} and {@link SimpleDateFormat}. Therefore the
  29. * formats used by this class must obey the same pattern rules as these Format
  30. * subclasses. This means that only legal number pattern characters ("0", "#",
  31. * ".", "," etc.) may appear in number formats. Other characters can be
  32. * inserted <em>before</em> or <em> after</em> the number pattern to form a
  33. * prefix or suffix.
  34. * </p>
  35. * <p>
  36. * For example the Excel pattern <code>"$#,##0.00 "USD"_);($#,##0.00 "USD")"
  37. * </code> will be correctly formatted as "$1,000.00 USD" or "($1,000.00 USD)".
  38. * However the pattern <code>"00-00-00"</code> is incorrectly formatted by
  39. * DecimalFormat as "000000--". For Excel formats that are not compatible with
  40. * DecimalFormat, you can provide your own custom {@link Format} implementation
  41. * via <code>DataFormatter.addFormat(String,Format)</code>. The following
  42. * custom formats are already provided by this class:
  43. * </p>
  44. * <pre>
  45. * <ul><li>SSN "000-00-0000"</li>
  46. * <li>Phone Number "(###) ###-####"</li>
  47. * <li>Zip plus 4 "00000-0000"</li>
  48. * </ul>
  49. * </pre>
  50. * <p>
  51. * If the Excel format pattern cannot be parsed successfully, then a default
  52. * format will be used. The default number format will mimic the Excel General
  53. * format: "#" for whole numbers and "#.##########" for decimal numbers. You
  54. * can override the default format pattern with <code>
  55. * DataFormatter.setDefaultNumberFormat(Format)</code>. <b>Note:</b> the
  56. * default format will only be used when a Format cannot be created from the
  57. * cell's data format string.
  58. *
  59. * @author James May (james dot may at fmr dot com)
  60. *
  61. */
  62. public class DataFormatter {
  63. /** Pattern to find a number format: "0" or "#" */
  64. private static final Pattern numPattern = Pattern.compile("[0#]+");
  65. /** Pattern to find days of week as text "ddd...." */
  66. private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE);
  67. /** Pattern to find "AM/PM" marker */
  68. private static final Pattern amPmPattern = Pattern.compile("((A|P)[M/P]*)", Pattern.CASE_INSENSITIVE);
  69. /** A regex to find patterns like [$$-1009] and [$?-452]. */
  70. private static final Pattern specialPatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])");
  71. /** <em>General</em> format for whole numbers. */
  72. private static final Format generalWholeNumFormat = new DecimalFormat("#");
  73. /** <em>General</em> format for decimal numbers. */
  74. private static final Format generalDecimalNumFormat = new DecimalFormat("#.##########");
  75. /** A default format to use when a number pattern cannot be parsed. */
  76. private Format defaultNumFormat;
  77. /**
  78. * A map to cache formats.
  79. * Map<String,Format> formats
  80. */
  81. private final Map formats;
  82. /**
  83. * Constructor
  84. */
  85. public DataFormatter() {
  86. formats = new HashMap();
  87. // init built-in formats
  88. Format zipFormat = ZipPlusFourFormat.instance;
  89. addFormat("00000\\-0000", zipFormat);
  90. addFormat("00000-0000", zipFormat);
  91. Format phoneFormat = PhoneFormat.instance;
  92. // allow for format string variations
  93. addFormat("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
  94. addFormat("[<=9999999]###-####;(###) ###-####", phoneFormat);
  95. addFormat("###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
  96. addFormat("###-####;(###) ###-####", phoneFormat);
  97. Format ssnFormat = SSNFormat.instance;
  98. addFormat("000\\-00\\-0000", ssnFormat);
  99. addFormat("000-00-0000", ssnFormat);
  100. }
  101. /**
  102. * Return a Format for the given cell if one exists, otherwise try to
  103. * create one. This method will return <code>null</code> if the any of the
  104. * following is true:
  105. * <ul>
  106. * <li>the cell's style is null</li>
  107. * <li>the style's data format string is null or empty</li>
  108. * <li>the format string cannot be recognized as either a number or date</li>
  109. * </ul>
  110. *
  111. * @param cell The cell to retrieve a Format for
  112. * @return A Format for the format String
  113. */
  114. private Format getFormat(Cell cell) {
  115. if ( cell.getCellStyle() == null) {
  116. return null;
  117. }
  118. int formatIndex = cell.getCellStyle().getDataFormat();
  119. String formatStr = cell.getCellStyle().getDataFormatString();
  120. if(formatStr == null || formatStr.trim().length() == 0) {
  121. return null;
  122. }
  123. return getFormat(cell.getNumericCellValue(), formatIndex, formatStr);
  124. }
  125. private Format getFormat(double cellValue, int formatIndex, String formatStr) {
  126. Format format = (Format)formats.get(formatStr);
  127. if (format != null) {
  128. return format;
  129. }
  130. if (formatStr.equals("General") || formatStr.equals("@")) {
  131. if (DataFormatter.isWholeNumber(cellValue)) {
  132. return generalWholeNumFormat;
  133. }
  134. return generalDecimalNumFormat;
  135. }
  136. format = createFormat(cellValue, formatIndex, formatStr);
  137. formats.put(formatStr, format);
  138. return format;
  139. }
  140. /**
  141. * Create and return a Format based on the format string from a cell's
  142. * style. If the pattern cannot be parsed, return a default pattern.
  143. *
  144. * @param cell The Excel cell
  145. * @return A Format representing the excel format. May return null.
  146. */
  147. public Format createFormat(Cell cell) {
  148. int formatIndex = cell.getCellStyle().getDataFormat();
  149. String formatStr = cell.getCellStyle().getDataFormatString();
  150. return createFormat(cell.getNumericCellValue(), formatIndex, formatStr);
  151. }
  152. private Format createFormat(double cellValue, int formatIndex, String sFormat) {
  153. // remove color formatting if present
  154. String formatStr = sFormat.replaceAll("\\[[a-zA-Z]*\\]", "");
  155. // try to extract special characters like currency
  156. Matcher m = specialPatternGroup.matcher(formatStr);
  157. while(m.find()) {
  158. String match = m.group();
  159. String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-'));
  160. if (symbol.indexOf('$') > -1) {
  161. StringBuffer sb = new StringBuffer();
  162. sb.append(symbol.substring(0, symbol.indexOf('$')));
  163. sb.append('\\');
  164. sb.append(symbol.substring(symbol.indexOf('$'), symbol.length()));
  165. symbol = sb.toString();
  166. }
  167. formatStr = m.replaceAll(symbol);
  168. m = specialPatternGroup.matcher(formatStr);
  169. }
  170. if(formatStr == null || formatStr.trim().length() == 0) {
  171. return getDefaultFormat(cellValue);
  172. }
  173. if(DateUtil.isADateFormat(formatIndex,formatStr) &&
  174. DateUtil.isValidExcelDate(cellValue)) {
  175. return createDateFormat(formatStr, cellValue);
  176. }
  177. if (numPattern.matcher(formatStr).find()) {
  178. return createNumberFormat(formatStr, cellValue);
  179. }
  180. // TODO - when does this occur?
  181. return null;
  182. }
  183. private Format createDateFormat(String pFormatStr, double cellValue) {
  184. String formatStr = pFormatStr;
  185. formatStr = formatStr.replaceAll("\\\\-","-");
  186. formatStr = formatStr.replaceAll("\\\\,",",");
  187. formatStr = formatStr.replaceAll("\\\\ "," ");
  188. formatStr = formatStr.replaceAll(";@", "");
  189. boolean hasAmPm = false;
  190. Matcher amPmMatcher = amPmPattern.matcher(formatStr);
  191. while (amPmMatcher.find()) {
  192. formatStr = amPmMatcher.replaceAll("@");
  193. hasAmPm = true;
  194. amPmMatcher = amPmPattern.matcher(formatStr);
  195. }
  196. formatStr = formatStr.replaceAll("@", "a");
  197. Matcher dateMatcher = daysAsText.matcher(formatStr);
  198. if (dateMatcher.find()) {
  199. String match = dateMatcher.group(0);
  200. formatStr = dateMatcher.replaceAll(match.toUpperCase().replaceAll("D", "E"));
  201. }
  202. // Convert excel date format to SimpleDateFormat.
  203. // Excel uses lower case 'm' for both minutes and months.
  204. // From Excel help:
  205. /*
  206. The "m" or "mm" code must appear immediately after the "h" or"hh"
  207. code or immediately before the "ss" code; otherwise, Microsoft
  208. Excel displays the month instead of minutes."
  209. */
  210. StringBuffer sb = new StringBuffer();
  211. char[] chars = formatStr.toCharArray();
  212. boolean mIsMonth = true;
  213. List ms = new ArrayList();
  214. for(int j=0; j<chars.length; j++) {
  215. char c = chars[j];
  216. if (c == 'h' || c == 'H') {
  217. mIsMonth = false;
  218. if (hasAmPm) {
  219. sb.append('h');
  220. } else {
  221. sb.append('H');
  222. }
  223. }
  224. else if (c == 'm') {
  225. if(mIsMonth) {
  226. sb.append('M');
  227. ms.add(
  228. new Integer(sb.length() -1)
  229. );
  230. } else {
  231. sb.append('m');
  232. }
  233. }
  234. else if (c == 's' || c == 'S') {
  235. sb.append('s');
  236. // if 'M' precedes 's' it should be minutes ('m')
  237. for (int i = 0; i < ms.size(); i++) {
  238. int index = ((Integer)ms.get(i)).intValue();
  239. if (sb.charAt(index) == 'M') {
  240. sb.replace(index, index+1, "m");
  241. }
  242. }
  243. mIsMonth = true;
  244. ms.clear();
  245. }
  246. else if (Character.isLetter(c)) {
  247. mIsMonth = true;
  248. ms.clear();
  249. if (c == 'y' || c == 'Y') {
  250. sb.append('y');
  251. }
  252. else if (c == 'd' || c == 'D') {
  253. sb.append('d');
  254. }
  255. else {
  256. sb.append(c);
  257. }
  258. }
  259. else {
  260. sb.append(c);
  261. }
  262. }
  263. formatStr = sb.toString();
  264. try {
  265. return new SimpleDateFormat(formatStr);
  266. } catch(IllegalArgumentException iae) {
  267. // the pattern could not be parsed correctly,
  268. // so fall back to the default number format
  269. return getDefaultFormat(cellValue);
  270. }
  271. }
  272. private Format createNumberFormat(String formatStr, double cellValue) {
  273. StringBuffer sb = new StringBuffer(formatStr);
  274. for (int i = 0; i < sb.length(); i++) {
  275. char c = sb.charAt(i);
  276. //handle (#,##0_);
  277. if (c == '(') {
  278. int idx = sb.indexOf(")", i);
  279. if (idx > -1 && sb.charAt(idx -1) == '_') {
  280. sb.deleteCharAt(idx);
  281. sb.deleteCharAt(idx - 1);
  282. sb.deleteCharAt(i);
  283. i--;
  284. }
  285. } else if (c == ')' && i > 0 && sb.charAt(i - 1) == '_') {
  286. sb.deleteCharAt(i);
  287. sb.deleteCharAt(i - 1);
  288. i--;
  289. // remove quotes and back slashes
  290. } else if (c == '\\' || c == '"') {
  291. sb.deleteCharAt(i);
  292. i--;
  293. // for scientific/engineering notation
  294. } else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') {
  295. sb.deleteCharAt(i);
  296. i--;
  297. }
  298. }
  299. try {
  300. return new DecimalFormat(sb.toString());
  301. } catch(IllegalArgumentException iae) {
  302. // the pattern could not be parsed correctly,
  303. // so fall back to the default number format
  304. return getDefaultFormat(cellValue);
  305. }
  306. }
  307. /**
  308. * Return true if the double value represents a whole number
  309. * @param d the double value to check
  310. * @return <code>true</code> if d is a whole number
  311. */
  312. private static boolean isWholeNumber(double d) {
  313. return d == Math.floor(d);
  314. }
  315. /**
  316. * Returns a default format for a cell.
  317. * @param cell The cell
  318. * @return a default format
  319. */
  320. public Format getDefaultFormat(Cell cell) {
  321. return getDefaultFormat(cell.getNumericCellValue());
  322. }
  323. private Format getDefaultFormat(double cellValue) {
  324. // for numeric cells try user supplied default
  325. if (defaultNumFormat != null) {
  326. return defaultNumFormat;
  327. // otherwise use general format
  328. }
  329. if (isWholeNumber(cellValue)){
  330. return generalWholeNumFormat;
  331. }
  332. return generalDecimalNumFormat;
  333. }
  334. /**
  335. * Returns the formatted value of an Excel date as a <tt>String</tt> based
  336. * on the cell's <code>DataFormat</code>. i.e. "Thursday, January 02, 2003"
  337. * , "01/02/2003" , "02-Jan" , etc.
  338. *
  339. * @param cell The cell
  340. * @return a formatted date string
  341. */
  342. private String getFormattedDateString(Cell cell) {
  343. Format dateFormat = getFormat(cell);
  344. Date d = cell.getDateCellValue();
  345. if (dateFormat != null) {
  346. return dateFormat.format(d);
  347. }
  348. return d.toString();
  349. }
  350. /**
  351. * Returns the formatted value of an Excel number as a <tt>String</tt>
  352. * based on the cell's <code>DataFormat</code>. Supported formats include
  353. * currency, percents, decimals, phone number, SSN, etc.:
  354. * "61.54%", "$100.00", "(800) 555-1234".
  355. *
  356. * @param cell The cell
  357. * @return a formatted number string
  358. */
  359. private String getFormattedNumberString(Cell cell) {
  360. Format numberFormat = getFormat(cell);
  361. double d = cell.getNumericCellValue();
  362. if (numberFormat == null) {
  363. return String.valueOf(d);
  364. }
  365. return numberFormat.format(new Double(d));
  366. }
  367. /**
  368. * Formats the given raw cell value, based on the supplied
  369. * format index and string, according to excel style rules.
  370. * @see #formatCellValue(Cell)
  371. */
  372. public String formatRawCellContents(double value, int formatIndex, String formatString) {
  373. // Is it a date?
  374. if(DateUtil.isADateFormat(formatIndex,formatString) &&
  375. DateUtil.isValidExcelDate(value)) {
  376. Format dateFormat = getFormat(value, formatIndex, formatString);
  377. Date d = DateUtil.getJavaDate(value);
  378. if (dateFormat == null) {
  379. return d.toString();
  380. }
  381. return dateFormat.format(d);
  382. }
  383. // else Number
  384. Format numberFormat = getFormat(value, formatIndex, formatString);
  385. if (numberFormat == null) {
  386. return String.valueOf(value);
  387. }
  388. return numberFormat.format(new Double(value));
  389. }
  390. /**
  391. * <p>
  392. * Returns the formatted value of a cell as a <tt>String</tt> regardless
  393. * of the cell type. If the Excel format pattern cannot be parsed then the
  394. * cell value will be formatted using a default format.
  395. * </p>
  396. * <p>When passed a null or blank cell, this method will return an empty
  397. * String (""). Formulas in formula type cells will not be evaluated.
  398. * </p>
  399. *
  400. * @param cell The cell
  401. * @return the formatted cell value as a String
  402. */
  403. public String formatCellValue(Cell cell) {
  404. return formatCellValue(cell, null);
  405. }
  406. /**
  407. * <p>
  408. * Returns the formatted value of a cell as a <tt>String</tt> regardless
  409. * of the cell type. If the Excel format pattern cannot be parsed then the
  410. * cell value will be formatted using a default format.
  411. * </p>
  412. * <p>When passed a null or blank cell, this method will return an empty
  413. * String (""). Formula cells will be evaluated using the given
  414. * {@link FormulaEvaluator} if the evaluator is non-null. If the
  415. * evaluator is null, then the formula String will be returned. The caller
  416. * is responsible for setting the currentRow on the evaluator
  417. *</p>
  418. *
  419. * @param cell The cell (can be null)
  420. * @param evaluator The FormulaEvaluator (can be null)
  421. * @return a string value of the cell
  422. */
  423. public String formatCellValue(Cell cell, FormulaEvaluator evaluator) {
  424. if (cell == null) {
  425. return "";
  426. }
  427. int cellType = cell.getCellType();
  428. if (cellType == Cell.CELL_TYPE_FORMULA) {
  429. if (evaluator == null) {
  430. return cell.getCellFormula();
  431. }
  432. cellType = evaluator.evaluateFormulaCell(cell);
  433. }
  434. switch (cellType) {
  435. case Cell.CELL_TYPE_NUMERIC :
  436. if (DateUtil.isCellDateFormatted(cell)) {
  437. return getFormattedDateString(cell);
  438. }
  439. return getFormattedNumberString(cell);
  440. case Cell.CELL_TYPE_STRING :
  441. return cell.getRichStringCellValue().getString();
  442. case Cell.CELL_TYPE_BOOLEAN :
  443. return String.valueOf(cell.getBooleanCellValue());
  444. case Cell.CELL_TYPE_BLANK :
  445. return "";
  446. }
  447. throw new RuntimeException("Unexpected celltype (" + cellType + ")");
  448. }
  449. /**
  450. * <p>
  451. * Sets a default number format to be used when the Excel format cannot be
  452. * parsed successfully. <b>Note:</b> This is a fall back for when an error
  453. * occurs while parsing an Excel number format pattern. This will not
  454. * affect cells with the <em>General</em> format.
  455. * </p>
  456. * <p>
  457. * The value that will be passed to the Format's format method (specified
  458. * by <code>java.text.Format#format</code>) will be a double value from a
  459. * numeric cell. Therefore the code in the format method should expect a
  460. * <code>Number</code> value.
  461. * </p>
  462. *
  463. * @param format A Format instance to be used as a default
  464. * @see java.text.Format#format
  465. */
  466. public void setDefaultNumberFormat(Format format) {
  467. Iterator itr = formats.entrySet().iterator();
  468. while(itr.hasNext()) {
  469. Map.Entry entry = (Map.Entry)itr.next();
  470. if (entry.getValue() == generalDecimalNumFormat
  471. || entry.getValue() == generalWholeNumFormat) {
  472. entry.setValue(format);
  473. }
  474. }
  475. defaultNumFormat = format;
  476. }
  477. /**
  478. * Adds a new format to the available formats.
  479. * <p>
  480. * The value that will be passed to the Format's format method (specified
  481. * by <code>java.text.Format#format</code>) will be a double value from a
  482. * numeric cell. Therefore the code in the format method should expect a
  483. * <code>Number</code> value.
  484. * </p>
  485. * @param excelFormatStr The data format string
  486. * @param format A Format instance
  487. */
  488. public void addFormat(String excelFormatStr, Format format) {
  489. formats.put(excelFormatStr, format);
  490. }
  491. // Some custom formats
  492. /**
  493. * @return a <tt>DecimalFormat</tt> with parseIntegerOnly set <code>true</code>
  494. */
  495. /* package */ static DecimalFormat createIntegerOnlyFormat(String fmt) {
  496. DecimalFormat result = new DecimalFormat(fmt);
  497. result.setParseIntegerOnly(true);
  498. return result;
  499. }
  500. /**
  501. * Format class for Excel's SSN format. This class mimics Excel's built-in
  502. * SSN formatting.
  503. *
  504. * @author James May
  505. */
  506. private static final class SSNFormat extends Format {
  507. public static final Format instance = new SSNFormat();
  508. private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
  509. private SSNFormat() {
  510. // enforce singleton
  511. }
  512. /** Format a number as an SSN */
  513. public static String format(Number num) {
  514. String result = df.format(num);
  515. StringBuffer sb = new StringBuffer();
  516. sb.append(result.substring(0, 3)).append('-');
  517. sb.append(result.substring(3, 5)).append('-');
  518. sb.append(result.substring(5, 9));
  519. return sb.toString();
  520. }
  521. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  522. return toAppendTo.append(format((Number)obj));
  523. }
  524. public Object parseObject(String source, ParsePosition pos) {
  525. return df.parseObject(source, pos);
  526. }
  527. }
  528. /**
  529. * Format class for Excel Zip + 4 format. This class mimics Excel's
  530. * built-in formatting for Zip + 4.
  531. * @author James May
  532. */
  533. private static final class ZipPlusFourFormat extends Format {
  534. public static final Format instance = new ZipPlusFourFormat();
  535. private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
  536. private ZipPlusFourFormat() {
  537. // enforce singleton
  538. }
  539. /** Format a number as Zip + 4 */
  540. public static String format(Number num) {
  541. String result = df.format(num);
  542. StringBuffer sb = new StringBuffer();
  543. sb.append(result.substring(0, 5)).append('-');
  544. sb.append(result.substring(5, 9));
  545. return sb.toString();
  546. }
  547. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  548. return toAppendTo.append(format((Number)obj));
  549. }
  550. public Object parseObject(String source, ParsePosition pos) {
  551. return df.parseObject(source, pos);
  552. }
  553. }
  554. /**
  555. * Format class for Excel phone number format. This class mimics Excel's
  556. * built-in phone number formatting.
  557. * @author James May
  558. */
  559. private static final class PhoneFormat extends Format {
  560. public static final Format instance = new PhoneFormat();
  561. private static final DecimalFormat df = createIntegerOnlyFormat("##########");
  562. private PhoneFormat() {
  563. // enforce singleton
  564. }
  565. /** Format a number as a phone number */
  566. public static String format(Number num) {
  567. String result = df.format(num);
  568. StringBuffer sb = new StringBuffer();
  569. String seg1, seg2, seg3;
  570. int len = result.length();
  571. if (len <= 4) {
  572. return result;
  573. }
  574. seg3 = result.substring(len - 4, len);
  575. seg2 = result.substring(Math.max(0, len - 7), len - 4);
  576. seg1 = result.substring(Math.max(0, len - 10), Math.max(0, len - 7));
  577. if(seg1 != null && seg1.trim().length() > 0) {
  578. sb.append('(').append(seg1).append(") ");
  579. }
  580. if(seg2 != null && seg2.trim().length() > 0) {
  581. sb.append(seg2).append('-');
  582. }
  583. sb.append(seg3);
  584. return sb.toString();
  585. }
  586. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  587. return toAppendTo.append(format((Number)obj));
  588. }
  589. public Object parseObject(String source, ParsePosition pos) {
  590. return df.parseObject(source, pos);
  591. }
  592. }
  593. }