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 56KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442
  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.beans.PropertyChangeSupport;
  20. import java.math.BigDecimal;
  21. import java.math.RoundingMode;
  22. import java.text.DateFormat;
  23. import java.text.DateFormatSymbols;
  24. import java.text.DecimalFormat;
  25. import java.text.DecimalFormatSymbols;
  26. import java.text.FieldPosition;
  27. import java.text.Format;
  28. import java.text.ParsePosition;
  29. import java.text.SimpleDateFormat;
  30. import java.util.ArrayList;
  31. import java.util.Date;
  32. import java.util.HashMap;
  33. import java.util.List;
  34. import java.util.Locale;
  35. import java.util.Map;
  36. import java.util.regex.Matcher;
  37. import java.util.regex.Pattern;
  38. import org.apache.logging.log4j.LogManager;
  39. import org.apache.logging.log4j.Logger;
  40. import org.apache.poi.ss.format.CellFormat;
  41. import org.apache.poi.ss.format.CellFormatResult;
  42. import org.apache.poi.ss.formula.ConditionalFormattingEvaluator;
  43. import org.apache.poi.ss.util.DateFormatConverter;
  44. import org.apache.poi.ss.util.NumberToTextConverter;
  45. import org.apache.poi.util.LocaleUtil;
  46. import org.apache.poi.util.StringUtil;
  47. /**
  48. * DataFormatter contains methods for formatting the value stored in a
  49. * Cell. This can be useful for reports and GUI presentations when you
  50. * need to display data exactly as it appears in Excel. Supported formats
  51. * include currency, SSN, percentages, decimals, dates, phone numbers, zip
  52. * codes, etc.
  53. * <p>
  54. * Internally, formats will be implemented using subclasses of {@link Format}
  55. * such as {@link DecimalFormat} and {@link SimpleDateFormat}. Therefore the
  56. * formats used by this class must obey the same pattern rules as these Format
  57. * subclasses. This means that only legal number pattern characters ("0", "#",
  58. * ".", "," etc.) may appear in number formats. Other characters can be
  59. * inserted <em>before</em> or <em> after</em> the number pattern to form a
  60. * prefix or suffix.
  61. * </p>
  62. * <p>
  63. * For example the Excel pattern {@code "$#,##0.00 "USD"_);($#,##0.00 "USD")"
  64. * } will be correctly formatted as "$1,000.00 USD" or "($1,000.00 USD)".
  65. * However the pattern {@code "00-00-00"} is incorrectly formatted by
  66. * DecimalFormat as "000000--". For Excel formats that are not compatible with
  67. * DecimalFormat, you can provide your own custom {@link Format} implementation
  68. * via {@code DataFormatter.addFormat(String,Format)}. The following
  69. * custom formats are already provided by this class:
  70. * </p>
  71. * <pre>{@code
  72. * SSN "000-00-0000"
  73. * Phone Number "(###) ###-####"
  74. * Zip plus 4 "00000-0000"
  75. * }</pre>
  76. * <p>
  77. * If the Excel format pattern cannot be parsed successfully, then a default
  78. * format will be used. The default number format will mimic the Excel General
  79. * format: "#" for whole numbers and "#.##########" for decimal numbers. You
  80. * can override the default format pattern with {@code
  81. * DataFormatter.setDefaultNumberFormat(Format)}. <b>Note:</b> the
  82. * default format will only be used when a Format cannot be created from the
  83. * cell's data format string.
  84. *
  85. * <p>
  86. * Note that by default formatted numeric values are trimmed.
  87. * Excel formats can contain spacers and padding and the default behavior is to strip them off.
  88. * </p>
  89. * <p>Example:</p>
  90. * <p>
  91. * Consider a numeric cell with a value {@code 12.343} and format {@code "##.##_ "}.
  92. * The trailing underscore and space ("_ ") in the format adds a space to the end and Excel formats this cell as {@code "12.34 "},
  93. * but {@code DataFormatter} trims the formatted value and returns {@code "12.34"}.
  94. * </p>
  95. * You can enable spaces by passing the {@code emulateCSV=true} flag in the {@code DateFormatter} cosntructor.
  96. * 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:
  97. * <ul>
  98. * <li>returned values are not trimmed</li>
  99. * <li>Invalid dates are formatted as 255 pound signs ("#")</li>
  100. * <li>simulate Excel's handling of a format string of all # when the value is 0.
  101. * Excel will output "", {@code DataFormatter} will output "0".
  102. * </ul>
  103. * <p>
  104. * Some formats are automatically "localized" by Excel, eg show as mm/dd/yyyy when
  105. * loaded in Excel in some Locales but as dd/mm/yyyy in others. These are always
  106. * returned in the "default" (US) format, as stored in the file.
  107. * Some format strings request an alternate locale, eg
  108. * {@code [$-809]d/m/yy h:mm AM/PM} which explicitly requests UK locale.
  109. * These locale directives are (currently) ignored.
  110. * You can use {@link DateFormatConverter} to do some of this localisation if
  111. * you need it.
  112. */
  113. @SuppressWarnings("unused")
  114. public class DataFormatter {
  115. private static final String defaultFractionWholePartFormat = "#";
  116. private static final String defaultFractionFractionPartFormat = "#/##";
  117. /** Pattern to find a number format: "0" or "#" */
  118. private static final Pattern numPattern = Pattern.compile("[0#]+");
  119. /** Pattern to find days of week as text "ddd...." */
  120. private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE);
  121. /** Pattern to find "AM/PM" marker */
  122. private static final Pattern amPmPattern = Pattern.compile("(([AP])[M/P]*)", Pattern.CASE_INSENSITIVE);
  123. /** Pattern to find formats with condition ranges e.g. [>=100] */
  124. private static final Pattern rangeConditionalPattern = Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*");
  125. /**
  126. * A regex to find locale patterns like [$$-1009] and [$?-452].
  127. * Note that we don't currently process these into locales
  128. */
  129. private static final Pattern localePatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+])");
  130. /**
  131. * A regex to match the colour formatting's rules.
  132. * Allowed colours are: Black, Blue, Cyan, Green,
  133. * Magenta, Red, White, Yellow, "Color n" (1<=n<=56)
  134. */
  135. private static final Pattern colorPattern =
  136. Pattern.compile("(\\[BLACK])|(\\[BLUE])|(\\[CYAN])|(\\[GREEN])|" +
  137. "(\\[MAGENTA])|(\\[RED])|(\\[WHITE])|(\\[YELLOW])|" +
  138. "(\\[COLOR\\s*\\d])|(\\[COLOR\\s*[0-5]\\d])", Pattern.CASE_INSENSITIVE);
  139. /**
  140. * A regex to identify a fraction pattern.
  141. * This requires that replaceAll("\\?", "#") has already been called
  142. */
  143. private static final Pattern fractionPattern = Pattern.compile("(?:([#\\d]+)\\s+)?(#+)\\s*/\\s*([#\\d]+)");
  144. /**
  145. * A regex to strip junk out of fraction formats
  146. */
  147. private static final Pattern fractionStripper = Pattern.compile("(\"[^\"]*\")|([^ ?#\\d/]+)");
  148. /**
  149. * A regex to detect if an alternate grouping character is used
  150. * in a numeric format
  151. */
  152. private static final Pattern alternateGrouping = Pattern.compile("([#0]([^.#0])[#0]{3})");
  153. /**
  154. * Cells formatted with a date or time format and which contain invalid date or time values
  155. * show 255 pound signs ("#").
  156. */
  157. private static final String invalidDateTimeString;
  158. static {
  159. StringBuilder buf = new StringBuilder();
  160. for(int i = 0; i < 255; i++) buf.append('#');
  161. invalidDateTimeString = buf.toString();
  162. }
  163. /**
  164. * The decimal symbols of the locale used for formatting values.
  165. */
  166. private DecimalFormatSymbols decimalSymbols;
  167. /**
  168. * The date symbols of the locale used for formatting values.
  169. */
  170. private DateFormatSymbols dateSymbols;
  171. /**
  172. * A default date format, if no date format was given
  173. */
  174. private DateFormat defaultDateformat;
  175. /** <em>General</em> format for numbers. */
  176. private Format generalNumberFormat;
  177. /** A default format to use when a number pattern cannot be parsed. */
  178. private Format defaultNumFormat;
  179. /**
  180. * A map to cache formats.
  181. * Map<String,Format> formats
  182. */
  183. private final Map<String,Format> formats = new HashMap<>();
  184. /** whether CSV friendly adjustments should be made to the formatted text **/
  185. private boolean emulateCSV = false;
  186. /** whether years in dates should be displayed with 4 digits even if the formatString specifies only 2 **/
  187. private boolean use4DigitYearsInAllDateFormats = false;
  188. /**
  189. * if set to true, avoid recalculating the values if there is a cached value available (default is false)
  190. */
  191. private boolean useCachedValuesForFormulaCells = false;
  192. /** stores the locale set by updateLocale method */
  193. private Locale locale;
  194. /** stores if the locale should change according to {@link LocaleUtil#getUserLocale()} */
  195. private boolean localeIsAdapting;
  196. // contain a support object instead of extending the support class
  197. private final PropertyChangeSupport pcs;
  198. /** For logging any problems we find */
  199. private static final Logger LOG = LogManager.getLogger(DataFormatter.class);
  200. /**
  201. * Creates a formatter using the {@link Locale#getDefault() default locale}.
  202. */
  203. public DataFormatter() {
  204. this(false);
  205. }
  206. /**
  207. * Creates a formatter using the {@link Locale#getDefault() default locale}.
  208. *
  209. * @param emulateCSV whether to emulate CSV output.
  210. */
  211. public DataFormatter(boolean emulateCSV) {
  212. this(LocaleUtil.getUserLocale(), true, emulateCSV);
  213. }
  214. /**
  215. * Creates a formatter using the given locale.
  216. */
  217. public DataFormatter(Locale locale) {
  218. this(locale, false);
  219. }
  220. /**
  221. * Creates a formatter using the given locale.
  222. *
  223. * @param emulateCSV whether to emulate CSV output.
  224. */
  225. public DataFormatter(Locale locale, boolean emulateCSV) {
  226. this(locale, false, emulateCSV);
  227. }
  228. /**
  229. * Creates a formatter using the given locale.
  230. * @param localeIsAdapting (true only if locale is not user-specified)
  231. * @param emulateCSV whether to emulate CSV output.
  232. */
  233. public DataFormatter(Locale locale, boolean localeIsAdapting, boolean emulateCSV) {
  234. this.localeIsAdapting = true;
  235. pcs = new PropertyChangeSupport(this);
  236. // localeIsAdapting must be true prior to this first checkForLocaleChange call.
  237. checkForLocaleChange(locale);
  238. // set localeIsAdapting so subsequent checks perform correctly
  239. // (whether a specific locale was provided to this DataFormatter or DataFormatter should
  240. // adapt to the current user locale as the locale changes)
  241. this.localeIsAdapting = localeIsAdapting;
  242. this.emulateCSV = emulateCSV;
  243. }
  244. /**
  245. * @param emulateCSV whether to emulate CSV output (default false).
  246. * @since POI 5.2.0
  247. */
  248. public void setEmulateCSV(boolean emulateCSV) {
  249. this.emulateCSV = emulateCSV;
  250. }
  251. /**
  252. * @return whether to emulate CSV output (default false).
  253. * @since POI 5.2.0
  254. */
  255. public boolean isEmulateCSV() {
  256. return emulateCSV;
  257. }
  258. /**
  259. * @param useCachedValuesForFormulaCells if set to true, when you do not provide a {@link FormulaEvaluator},
  260. * for cells with formulas, we will return the cached value for the cell (if available),
  261. * otherwise - we return the formula itself.
  262. * The default is false and this means we return the formula itself.
  263. * @since POI 5.2.0
  264. */
  265. public void setUseCachedValuesForFormulaCells(boolean useCachedValuesForFormulaCells) {
  266. this.useCachedValuesForFormulaCells = useCachedValuesForFormulaCells;
  267. }
  268. /**
  269. * @return useCachedValuesForFormulaCells if set to true, when you do not provide a {@link FormulaEvaluator},
  270. * for cells with formulas, we will return the cached value for the cell (if available),
  271. * otherwise - we return the formula itself.
  272. * The default is false and this means we return the formula itself.
  273. * @since POI 5.2.0
  274. */
  275. public boolean useCachedValuesForFormulaCells() {
  276. return useCachedValuesForFormulaCells;
  277. }
  278. /**
  279. * @param use4DigitYearsInAllDateFormats set to true if you want to have all dates formatted with 4 digit
  280. * years (even if the format associated with the cell specifies just 2)
  281. * @since POI 5.2.0
  282. */
  283. public void setUse4DigitYearsInAllDateFormats(boolean use4DigitYearsInAllDateFormats) {
  284. this.use4DigitYearsInAllDateFormats = use4DigitYearsInAllDateFormats;
  285. }
  286. /**
  287. * @return use4DigitYearsInAllDateFormats set to true if you want to have all dates formatted with 4 digit
  288. * years (even if the format associated with the cell specifies just 2)
  289. * @since POI 5.2.0
  290. */
  291. public boolean use4DigitYearsInAllDateFormats() {
  292. return use4DigitYearsInAllDateFormats;
  293. }
  294. /**
  295. * Return a Format for the given cell if one exists, otherwise try to
  296. * create one. This method will return {@code null} if any of the
  297. * following is true:
  298. * <ul>
  299. * <li>the cell's style is null</li>
  300. * <li>the style's data format string is null or empty</li>
  301. * <li>the format string cannot be recognized as either a number or date</li>
  302. * </ul>
  303. *
  304. * @param cell The cell to retrieve a Format for
  305. * @return A Format for the format String
  306. */
  307. private Format getFormat(Cell cell, ConditionalFormattingEvaluator cfEvaluator) {
  308. if (cell == null) return null;
  309. ExcelNumberFormat numFmt = ExcelNumberFormat.from(cell, cfEvaluator);
  310. if ( numFmt == null) {
  311. return null;
  312. }
  313. int formatIndex = numFmt.getIdx();
  314. String formatStr = numFmt.getFormat();
  315. if(StringUtil.isBlank(formatStr)) {
  316. return null;
  317. }
  318. return getFormat(cell.getNumericCellValue(), formatIndex, formatStr, isDate1904(cell));
  319. }
  320. private boolean isDate1904(Cell cell) {
  321. if ( cell != null && cell.getSheet().getWorkbook() instanceof Date1904Support) {
  322. return ((Date1904Support)cell.getSheet().getWorkbook()).isDate1904();
  323. }
  324. return false;
  325. }
  326. private Format getFormat(double cellValue, int formatIndex, String formatStrIn, boolean use1904Windowing) {
  327. if (formatStrIn == null) {
  328. throw new IllegalArgumentException("Missing input format for value " + cellValue + " and index " + formatIndex);
  329. }
  330. checkForLocaleChange();
  331. // Might be better to separate out the n p and z formats, falling back to p when n and z are not set.
  332. // That however would require other code to be re factored.
  333. // String[] formatBits = formatStrIn.split(";");
  334. // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2;
  335. // String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0];
  336. // this replace is done to fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63211
  337. String formatStr = formatStrIn.replace("\\%", "\'%\'");
  338. // Excel supports 2+ part conditional data formats, eg positive/negative/zero,
  339. // or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds
  340. // of different formats for different ranges, just +ve/-ve, we need to
  341. // handle these ourselves in a special way.
  342. // For now, if we detect 2+ parts, we call out to CellFormat to handle it
  343. // TODO Going forward, we should really merge the logic between the two classes
  344. if (formatStr.contains(";") &&
  345. (formatStr.indexOf(';') != formatStr.lastIndexOf(';')
  346. || rangeConditionalPattern.matcher(formatStr).matches()
  347. ) ) {
  348. try {
  349. // Ask CellFormat to get a formatter for it
  350. CellFormat cfmt = CellFormat.getInstance(locale, formatStr);
  351. // CellFormat requires callers to identify date vs not, so do so
  352. // don't try to handle Date value 0, let a 3 or 4-part format take care of it
  353. Object cellValueO = (cellValue != 0.0 && DateUtil.isADateFormat(formatIndex, formatStr))
  354. ? DateUtil.getJavaDate(cellValue, use1904Windowing)
  355. : cellValue;
  356. // Wrap and return (non-cacheable - CellFormat does that)
  357. return new CellFormatResultWrapper( cfmt.apply(cellValueO) );
  358. } catch (Exception e) {
  359. LOG.atWarn().withThrowable(e).log("Formatting failed for format {}, falling back", formatStr);
  360. }
  361. }
  362. // Excel's # with value 0 will output empty where Java will output 0. This hack removes the # from the format.
  363. if (emulateCSV && cellValue == 0.0 && formatStr.contains("#") && !formatStr.contains("0")) {
  364. formatStr = formatStr.replace("#", "");
  365. }
  366. // See if we already have it cached
  367. Format format = formats.get(formatStr);
  368. if (format != null) {
  369. return format;
  370. }
  371. // Is it one of the special built in types, General or @?
  372. if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
  373. return generalNumberFormat;
  374. }
  375. // Build a formatter, and cache it
  376. format = createFormat(cellValue, formatIndex, formatStr);
  377. formats.put(formatStr, format);
  378. return format;
  379. }
  380. /**
  381. * Create and return a Format based on the format string from a cell's
  382. * style. If the pattern cannot be parsed, return a default pattern.
  383. *
  384. * @param cell The Excel cell
  385. * @return A Format representing the excel format. May return null.
  386. */
  387. public Format createFormat(Cell cell) {
  388. int formatIndex = cell.getCellStyle().getDataFormat();
  389. String formatStr = cell.getCellStyle().getDataFormatString();
  390. return createFormat(cell.getNumericCellValue(), formatIndex, formatStr);
  391. }
  392. private Format createFormat(double cellValue, int formatIndex, String sFormat) {
  393. checkForLocaleChange();
  394. String formatStr = sFormat;
  395. // Remove colour formatting if present
  396. if (formatStr != null) {
  397. Matcher colourM = colorPattern.matcher(formatStr);
  398. while (colourM.find()) {
  399. String colour = colourM.group();
  400. // Paranoid replacement...
  401. int at = formatStr.indexOf(colour);
  402. if (at == -1) break;
  403. String nFormatStr = formatStr.substring(0, at) +
  404. formatStr.substring(at + colour.length());
  405. if (nFormatStr.equals(formatStr)) break;
  406. // Try again in case there's multiple
  407. formatStr = nFormatStr;
  408. colourM = colorPattern.matcher(formatStr);
  409. }
  410. }
  411. // Strip off the locale information, we use an instance-wide locale for everything
  412. if (formatStr != null) {
  413. Matcher m = localePatternGroup.matcher(formatStr);
  414. while (m.find()) {
  415. String match = m.group();
  416. String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-'));
  417. if (symbol.indexOf('$') > -1) {
  418. symbol = symbol.substring(0, symbol.indexOf('$')) +
  419. '\\' +
  420. symbol.substring(symbol.indexOf('$'));
  421. }
  422. formatStr = m.replaceAll(symbol);
  423. m = localePatternGroup.matcher(formatStr);
  424. }
  425. }
  426. // Check for special cases
  427. if(StringUtil.isBlank(formatStr)) {
  428. return getDefaultFormat(cellValue);
  429. }
  430. if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
  431. return generalNumberFormat;
  432. }
  433. if(DateUtil.isADateFormat(formatIndex,formatStr) &&
  434. DateUtil.isValidExcelDate(cellValue)) {
  435. return createDateFormat(formatStr, cellValue);
  436. }
  437. // Excel supports fractions in format strings, which Java doesn't
  438. if (formatStr.contains("#/") || formatStr.contains("?/")) {
  439. String[] chunks = formatStr.split(";");
  440. for (String chunk1 : chunks) {
  441. String chunk = chunk1.replace("?", "#");
  442. Matcher matcher = fractionStripper.matcher(chunk);
  443. chunk = matcher.replaceAll(" ");
  444. chunk = chunk.replaceAll(" +", " ");
  445. Matcher fractionMatcher = fractionPattern.matcher(chunk);
  446. //take the first match
  447. if (fractionMatcher.find()) {
  448. String wholePart = (fractionMatcher.group(1) == null) ? "" : defaultFractionWholePartFormat;
  449. return new FractionFormat(wholePart, fractionMatcher.group(3));
  450. }
  451. }
  452. // Strip custom text in quotes and escaped characters for now as it can cause performance problems in fractions.
  453. //String strippedFormatStr = formatStr.replaceAll("\\\\ ", " ").replaceAll("\\\\.", "").replaceAll("\"[^\"]*\"", " ").replaceAll("\\?", "#");
  454. return new FractionFormat(defaultFractionWholePartFormat, defaultFractionFractionPartFormat);
  455. }
  456. if (numPattern.matcher(formatStr).find()) {
  457. return createNumberFormat(formatStr, cellValue);
  458. }
  459. if (emulateCSV) {
  460. return new ConstantStringFormat(cleanFormatForNumber(formatStr));
  461. }
  462. // TODO - when does this occur?
  463. return null;
  464. }
  465. String adjustTo4DigitYearsIfConfigured(String format) {
  466. if (use4DigitYearsInAllDateFormats) {
  467. int ypos2 = format.indexOf("yy");
  468. if (ypos2 < 0) {
  469. return format;
  470. } else {
  471. int ypos3 = format.indexOf("yyy");
  472. int ypos4 = format.indexOf("yyyy");
  473. if (ypos4 == ypos2) {
  474. String part1 = format.substring(0, ypos2 + 4);
  475. String part2 = format.substring(ypos2 + 4);
  476. return part1 + adjustTo4DigitYearsIfConfigured(part2);
  477. } else if (ypos3 == ypos2) {
  478. return format;
  479. } else {
  480. String part1 = format.substring(0, ypos2 + 2);
  481. String part2 = format.substring(ypos2 + 2);
  482. return part1 + "yy" + adjustTo4DigitYearsIfConfigured(part2);
  483. }
  484. }
  485. }
  486. return format;
  487. }
  488. private Format createDateFormat(String pFormatStr, double cellValue) {
  489. String formatStr = adjustTo4DigitYearsIfConfigured(pFormatStr);
  490. formatStr = formatStr.replace("\\-","-");
  491. formatStr = formatStr.replace("\\,",",");
  492. formatStr = formatStr.replace("\\.","."); // . is a special regexp char
  493. formatStr = formatStr.replace("\\ "," ");
  494. formatStr = formatStr.replace("\\/","/"); // weird: m\\/d\\/yyyy
  495. formatStr = formatStr.replace(";@", "");
  496. formatStr = formatStr.replace("\"/\"", "/"); // "/" is escaped for no reason in: mm"/"dd"/"yyyy
  497. formatStr = formatStr.replace("\"\"", "'"); // replace Excel quoting with Java style quoting
  498. formatStr = formatStr.replace("\\T","'T'"); // Quote the T is iso8601 style dates
  499. boolean hasAmPm = false;
  500. Matcher amPmMatcher = amPmPattern.matcher(formatStr);
  501. while (amPmMatcher.find()) {
  502. formatStr = amPmMatcher.replaceAll("@");
  503. hasAmPm = true;
  504. amPmMatcher = amPmPattern.matcher(formatStr);
  505. }
  506. formatStr = formatStr.replace('@', 'a');
  507. Matcher dateMatcher = daysAsText.matcher(formatStr);
  508. if (dateMatcher.find()) {
  509. String match = dateMatcher.group(0).toUpperCase(Locale.ROOT).replace('D', 'E');
  510. formatStr = dateMatcher.replaceAll(match);
  511. }
  512. // Convert excel date format to SimpleDateFormat.
  513. // Excel uses lower and upper case 'm' for both minutes and months.
  514. // From Excel help:
  515. /*
  516. The "m" or "mm" code must appear immediately after the "h" or"hh"
  517. code or immediately before the "ss" code; otherwise, Microsoft
  518. Excel displays the month instead of minutes."
  519. */
  520. StringBuilder sb = new StringBuilder();
  521. char[] chars = formatStr.toCharArray();
  522. boolean mIsMonth = true;
  523. List<Integer> ms = new ArrayList<>();
  524. boolean isElapsed = false;
  525. for(int j=0; j<chars.length; j++) {
  526. char c = chars[j];
  527. if (c == '\'') {
  528. sb.append(c);
  529. j++;
  530. // skip until the next quote
  531. while(j<chars.length) {
  532. c = chars[j];
  533. sb.append(c);
  534. if(c == '\'') {
  535. break;
  536. }
  537. j++;
  538. }
  539. }
  540. else if (c == '[' && !isElapsed) {
  541. isElapsed = true;
  542. mIsMonth = false;
  543. sb.append(c);
  544. }
  545. else if (c == ']' && isElapsed) {
  546. isElapsed = false;
  547. sb.append(c);
  548. }
  549. else if (isElapsed) {
  550. if (c == 'h' || c == 'H') {
  551. sb.append('H');
  552. }
  553. else if (c == 'm' || c == 'M') {
  554. sb.append('m');
  555. }
  556. else if (c == 's' || c == 'S') {
  557. sb.append('s');
  558. }
  559. else {
  560. sb.append(c);
  561. }
  562. }
  563. else if (c == 'h' || c == 'H') {
  564. mIsMonth = false;
  565. if (hasAmPm) {
  566. sb.append('h');
  567. } else {
  568. sb.append('H');
  569. }
  570. }
  571. else if (c == 'm' || c == 'M') {
  572. if(mIsMonth) {
  573. sb.append('M');
  574. ms.add(sb.length() - 1);
  575. } else {
  576. sb.append('m');
  577. }
  578. }
  579. else if (c == 's' || c == 'S') {
  580. sb.append('s');
  581. // if 'M' precedes 's' it should be minutes ('m')
  582. for (int index : ms) {
  583. if (sb.charAt(index) == 'M') {
  584. sb.replace(index, index + 1, "m");
  585. }
  586. }
  587. mIsMonth = true;
  588. ms.clear();
  589. }
  590. else if (Character.isLetter(c)) {
  591. mIsMonth = true;
  592. ms.clear();
  593. if (c == 'y' || c == 'Y') {
  594. sb.append('y');
  595. }
  596. else if (c == 'd' || c == 'D') {
  597. sb.append('d');
  598. }
  599. else {
  600. sb.append(c);
  601. }
  602. }
  603. else {
  604. if (Character.isWhitespace(c)){
  605. ms.clear();
  606. }
  607. sb.append(c);
  608. }
  609. }
  610. formatStr = sb.toString();
  611. try {
  612. return new ExcelStyleDateFormatter(formatStr, dateSymbols);
  613. } catch(IllegalArgumentException iae) {
  614. LOG.atDebug().withThrowable(iae).log("Formatting failed for format {}, falling back", formatStr);
  615. // the pattern could not be parsed correctly,
  616. // so fall back to the default number format
  617. return getDefaultFormat(cellValue);
  618. }
  619. }
  620. private String cleanFormatForNumber(String formatStrIn) {
  621. // this replace is done to fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63211
  622. String formatStr = formatStrIn.replace("\\%", "\'%\'");
  623. StringBuilder sb = new StringBuilder(formatStr);
  624. if (emulateCSV) {
  625. // Requested spacers with "_" are replaced by a single space.
  626. // Full-column-width padding "*" are removed.
  627. // Not processing fractions at this time. Replace ? with space.
  628. // This matches CSV output.
  629. for (int i = 0; i < sb.length(); i++) {
  630. char c = sb.charAt(i);
  631. if (c == '_' || c == '*' || c == '?') {
  632. if (i > 0 && sb.charAt((i - 1)) == '\\') {
  633. // It's escaped, don't worry
  634. continue;
  635. }
  636. if (c == '?') {
  637. sb.setCharAt(i, ' ');
  638. } else if (i < sb.length() - 1) {
  639. // Remove the character we're supposed
  640. // to match the space of / pad to the
  641. // column width with
  642. if (c == '_') {
  643. sb.setCharAt(i + 1, ' ');
  644. } else {
  645. sb.deleteCharAt(i + 1);
  646. }
  647. // Remove the character too
  648. sb.deleteCharAt(i);
  649. i--;
  650. }
  651. }
  652. }
  653. } else {
  654. // If they requested spacers, with "_",
  655. // remove those as we don't do spacing
  656. // If they requested full-column-width
  657. // padding, with "*", remove those too
  658. for (int i = 0; i < sb.length(); i++) {
  659. char c = sb.charAt(i);
  660. if (c == '_' || c == '*') {
  661. if (i > 0 && sb.charAt((i - 1)) == '\\') {
  662. // It's escaped, don't worry
  663. continue;
  664. }
  665. if (i < sb.length() - 1) {
  666. // Remove the character we're supposed
  667. // to match the space of / pad to the
  668. // column width with
  669. sb.deleteCharAt(i + 1);
  670. }
  671. // Remove the _ too
  672. sb.deleteCharAt(i);
  673. i--;
  674. }
  675. }
  676. }
  677. // Now, handle the other aspects like
  678. // quoting and scientific notation
  679. for(int i = 0; i < sb.length(); i++) {
  680. char c = sb.charAt(i);
  681. // remove quotes and back slashes
  682. if (c == '\\' || c == '"') {
  683. sb.deleteCharAt(i);
  684. i--;
  685. // for scientific/engineering notation
  686. } else if ((c == '+' || c == '-') && i > 0 && sb.charAt(i - 1) == 'E') {
  687. sb.deleteCharAt(i);
  688. i--;
  689. }
  690. }
  691. return sb.toString();
  692. }
  693. private static class InternalDecimalFormatWithScale extends Format {
  694. private static final Pattern endsWithCommas = Pattern.compile("(,+)$");
  695. private final BigDecimal divider;
  696. private static final BigDecimal ONE_THOUSAND = BigDecimal.valueOf(1000);
  697. private final DecimalFormat df;
  698. private static String trimTrailingCommas(String s) {
  699. return s.replaceAll(",+$", "");
  700. }
  701. public InternalDecimalFormatWithScale(String pattern, DecimalFormatSymbols symbols) {
  702. df = new DecimalFormat(trimTrailingCommas(pattern), symbols);
  703. setExcelStyleRoundingMode(df);
  704. Matcher endsWithCommasMatcher = endsWithCommas.matcher(pattern);
  705. if (endsWithCommasMatcher.find()) {
  706. String commas = (endsWithCommasMatcher.group(1));
  707. BigDecimal temp = BigDecimal.ONE;
  708. for (int i = 0; i < commas.length(); ++i) {
  709. temp = temp.multiply(ONE_THOUSAND);
  710. }
  711. divider = temp;
  712. } else {
  713. divider = null;
  714. }
  715. }
  716. private Object scaleInput(Object obj) {
  717. if (divider != null) {
  718. if (obj instanceof BigDecimal) {
  719. obj = ((BigDecimal) obj).divide(divider, RoundingMode.HALF_UP);
  720. } else if (obj instanceof Double) {
  721. obj = (Double) obj / divider.doubleValue();
  722. } else {
  723. throw new UnsupportedOperationException();
  724. }
  725. }
  726. return obj;
  727. }
  728. @Override
  729. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  730. obj = scaleInput(obj);
  731. return df.format(obj, toAppendTo, pos);
  732. }
  733. @Override
  734. public Object parseObject(String source, ParsePosition pos) {
  735. throw new UnsupportedOperationException();
  736. }
  737. }
  738. private Format createNumberFormat(String formatStr, double cellValue) {
  739. String format = cleanFormatForNumber(formatStr);
  740. DecimalFormatSymbols symbols = decimalSymbols;
  741. // Do we need to change the grouping character?
  742. // eg for a format like #'##0 which wants 12'345 not 12,345
  743. Matcher agm = alternateGrouping.matcher(format);
  744. if (agm.find()) {
  745. char grouping = agm.group(2).charAt(0);
  746. // Only replace the grouping character if it is not the default
  747. // grouping character for the US locale (',') in order to enable
  748. // correct grouping for non-US locales.
  749. if (grouping!=',') {
  750. symbols = DecimalFormatSymbols.getInstance(locale);
  751. symbols.setGroupingSeparator(grouping);
  752. String oldPart = agm.group(1);
  753. String newPart = oldPart.replace(grouping, ',');
  754. format = format.replace(oldPart, newPart);
  755. }
  756. }
  757. try {
  758. return new InternalDecimalFormatWithScale(format, symbols);
  759. } catch(IllegalArgumentException iae) {
  760. LOG.atDebug().withThrowable(iae).log("Formatting failed for format {}, falling back", formatStr);
  761. // the pattern could not be parsed correctly,
  762. // so fall back to the default number format
  763. return getDefaultFormat(cellValue);
  764. }
  765. }
  766. /**
  767. * Returns a default format for a cell.
  768. * @param cell The cell
  769. * @return a default format
  770. */
  771. public Format getDefaultFormat(Cell cell) {
  772. return getDefaultFormat(cell.getNumericCellValue());
  773. }
  774. private Format getDefaultFormat(double cellValue) {
  775. checkForLocaleChange();
  776. // for numeric cells try user supplied default
  777. if (defaultNumFormat != null) {
  778. return defaultNumFormat;
  779. // otherwise use general format
  780. }
  781. return generalNumberFormat;
  782. }
  783. /**
  784. * Performs Excel-style date formatting, using the
  785. * supplied Date and format
  786. */
  787. @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
  788. private String performDateFormatting(Date d, Format dateFormat) {
  789. Format df = dateFormat != null ? dateFormat : defaultDateformat;
  790. synchronized (df) {
  791. return df.format(d);
  792. }
  793. }
  794. /**
  795. * Returns the formatted value of an Excel date as a {@code String} based
  796. * on the cell's {@code DataFormat}. i.e. "Thursday, January 02, 2003"
  797. * , "01/02/2003" , "02-Jan" , etc.
  798. * <p>
  799. * If any conditional format rules apply, the highest priority with a number format is used.
  800. * If no rules contain a number format, or no rules apply, the cell's style format is used.
  801. * If the style does not have a format, the default date format is applied.
  802. *
  803. * @param cell to format
  804. * @param cfEvaluator ConditionalFormattingEvaluator (if available)
  805. * @return Formatted value
  806. */
  807. @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
  808. private String getFormattedDateString(Cell cell, ConditionalFormattingEvaluator cfEvaluator) {
  809. if (cell == null) {
  810. return null;
  811. }
  812. Format dateFormat = getFormat(cell, cfEvaluator);
  813. if (dateFormat == null) {
  814. if (defaultDateformat == null) {
  815. DateFormatSymbols sym = DateFormatSymbols.getInstance(LocaleUtil.getUserLocale());
  816. SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", sym);
  817. sdf.setTimeZone(LocaleUtil.getUserTimeZone());
  818. dateFormat = sdf;
  819. } else {
  820. dateFormat = defaultNumFormat;
  821. }
  822. }
  823. synchronized (dateFormat) {
  824. if(dateFormat instanceof ExcelStyleDateFormatter) {
  825. // Hint about the raw excel value
  826. ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(
  827. cell.getNumericCellValue()
  828. );
  829. }
  830. Date d = cell.getDateCellValue();
  831. return performDateFormatting(d, dateFormat);
  832. }
  833. }
  834. /**
  835. * Returns the formatted value of an Excel number as a {@code String}
  836. * based on the cell's {@code DataFormat}. Supported formats include
  837. * currency, percents, decimals, phone number, SSN, etc.:
  838. * "61.54%", "$100.00", "(800) 555-1234".
  839. * <p>
  840. * Format comes from either the highest priority conditional format rule with a
  841. * specified format, or from the cell style.
  842. *
  843. * @param cell The cell
  844. * @param cfEvaluator if available, or null
  845. * @return a formatted number string
  846. */
  847. private String getFormattedNumberString(Cell cell, ConditionalFormattingEvaluator cfEvaluator) {
  848. if (cell == null) {
  849. return null;
  850. }
  851. Format numberFormat = getFormat(cell, cfEvaluator);
  852. double d = cell.getNumericCellValue();
  853. if (numberFormat == null) {
  854. return Double.toString(d);
  855. }
  856. String formatted;
  857. try {
  858. //see https://github.com/apache/poi/pull/321 -- but this sometimes fails, thus the catch and retry
  859. formatted = numberFormat.format(BigDecimal.valueOf(d));
  860. } catch (NumberFormatException nfe) {
  861. formatted = numberFormat.format(d);
  862. }
  863. return formatted.replaceFirst("E(\\d)", "E+$1"); // to match Excel's E-notation
  864. }
  865. /**
  866. * Formats the given raw cell value, based on the supplied
  867. * format index and string, according to excel style rules.
  868. * @see #formatCellValue(Cell)
  869. */
  870. public String formatRawCellContents(double value, int formatIndex, String formatString) {
  871. return formatRawCellContents(value, formatIndex, formatString, false);
  872. }
  873. /**
  874. * Formats the given raw cell value, based on the supplied
  875. * format index and string, according to excel style rules.
  876. * @see #formatCellValue(Cell)
  877. */
  878. public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) {
  879. checkForLocaleChange();
  880. // Is it a date?
  881. if(DateUtil.isADateFormat(formatIndex,formatString)) {
  882. if(DateUtil.isValidExcelDate(value)) {
  883. Format dateFormat = getFormat(value, formatIndex, formatString, use1904Windowing);
  884. if(dateFormat instanceof ExcelStyleDateFormatter) {
  885. // Hint about the raw excel value
  886. ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(value);
  887. }
  888. Date d = DateUtil.getJavaDate(value, use1904Windowing);
  889. return performDateFormatting(d, dateFormat);
  890. }
  891. // RK: Invalid dates are 255 #s.
  892. if (emulateCSV) {
  893. return invalidDateTimeString;
  894. }
  895. }
  896. // else Number
  897. Format numberFormat = getFormat(value, formatIndex, formatString, use1904Windowing);
  898. if (numberFormat == null) {
  899. return String.valueOf(value);
  900. }
  901. // When formatting 'value', double to text to BigDecimal produces more
  902. // accurate results than double to Double in JDK8 (as compared to
  903. // previous versions). However, if the value contains E notation, this
  904. // would expand the values, which we do not want, so revert to
  905. // original method.
  906. String result;
  907. final String textValue = NumberToTextConverter.toText(value);
  908. if (textValue.indexOf('E') > -1) {
  909. result = numberFormat.format(value);
  910. }
  911. else {
  912. result = numberFormat.format(new BigDecimal(textValue));
  913. }
  914. // If they requested a non-abbreviated Scientific format,
  915. // and there's an E## (but not E-##), add the missing '+' for E+##
  916. String fslc = formatString.toLowerCase(Locale.ROOT);
  917. if ((fslc.contains("general") || fslc.contains("e+0"))
  918. && result.contains("E") && !result.contains("E-")) {
  919. result = result.replaceFirst("E", "E+");
  920. }
  921. return result;
  922. }
  923. /**
  924. * <p>
  925. * Returns the formatted value of a cell as a {@code String} regardless
  926. * of the cell type. If the Excel format pattern cannot be parsed then the
  927. * cell value will be formatted using a default format.
  928. * </p>
  929. * <p>When passed a null or blank cell, this method will return an empty
  930. * String (""). Formulas in formula type cells will not be evaluated.
  931. * {@link #setUseCachedValuesForFormulaCells} controls how these cells are evaluated.
  932. * </p>
  933. *
  934. * @param cell The cell
  935. * @return the formatted cell value as a String
  936. * @see #setUseCachedValuesForFormulaCells(boolean)
  937. * @see #formatCellValue(Cell, FormulaEvaluator)
  938. * @see #formatCellValue(Cell, FormulaEvaluator, ConditionalFormattingEvaluator)
  939. */
  940. public String formatCellValue(Cell cell) {
  941. return formatCellValue(cell, null);
  942. }
  943. /**
  944. * <p>
  945. * Returns the formatted value of a cell as a {@code String} regardless
  946. * of the cell type. If the Excel number format pattern cannot be parsed then the
  947. * cell value will be formatted using a default format.
  948. * </p>
  949. * <p>When passed a null or blank cell, this method will return an empty
  950. * String (""). Formula cells will be evaluated using the given
  951. * {@link FormulaEvaluator} if the evaluator is non-null. If the
  952. * evaluator is null, then the formula String will be returned. The caller
  953. * is responsible for setting the currentRow on the evaluator.
  954. *</p>
  955. *
  956. * @param cell The cell (can be null)
  957. * @param evaluator The FormulaEvaluator (can be null)
  958. * @return a string value of the cell
  959. * @see #formatCellValue(Cell)
  960. * @see #formatCellValue(Cell, FormulaEvaluator, ConditionalFormattingEvaluator)
  961. */
  962. public String formatCellValue(Cell cell, FormulaEvaluator evaluator) {
  963. return formatCellValue(cell, evaluator, null);
  964. }
  965. /**
  966. * <p>
  967. * Returns the formatted value of a cell as a {@code String} regardless
  968. * of the cell type. If the Excel number format pattern cannot be parsed then the
  969. * cell value will be formatted using a default format.
  970. * </p>
  971. * <p>When passed a null or blank cell, this method will return an empty
  972. * String (""). Formula cells will be evaluated using the given
  973. * {@link FormulaEvaluator} if the evaluator is non-null. If the
  974. * evaluator is null, then the formula String will be returned. The caller
  975. * is responsible for setting the currentRow on the evaluator
  976. *</p>
  977. * <p>
  978. * When a ConditionalFormattingEvaluator is present, it is checked first to see
  979. * if there is a number format to apply. If multiple rules apply, the last one is used.
  980. * If no ConditionalFormattingEvaluator is present, no rules apply, or the applied
  981. * rules do not define a format, the cell's style format is used.
  982. * </p>
  983. * <p>
  984. * The two evaluators should be from the same context, to avoid inconsistencies in cached values.
  985. *</p>
  986. *
  987. * @param cell The cell (can be null)
  988. * @param evaluator The FormulaEvaluator (can be null)
  989. * @param cfEvaluator ConditionalFormattingEvaluator (can be null)
  990. * @return a string value of the cell
  991. * @see #formatCellValue(Cell)
  992. * @see #formatCellValue(Cell, FormulaEvaluator)
  993. */
  994. public String formatCellValue(Cell cell, FormulaEvaluator evaluator, ConditionalFormattingEvaluator cfEvaluator) {
  995. checkForLocaleChange();
  996. if (cell == null) {
  997. return "";
  998. }
  999. CellType cellType = cell.getCellType();
  1000. if (cellType == CellType.FORMULA) {
  1001. if (evaluator == null) {
  1002. if (useCachedValuesForFormulaCells) {
  1003. try {
  1004. cellType = cell.getCachedFormulaResultType();
  1005. } catch (Exception e) {
  1006. return cell.getCellFormula();
  1007. }
  1008. } else {
  1009. return cell.getCellFormula();
  1010. }
  1011. } else {
  1012. cellType = evaluator.evaluateFormulaCell(cell);
  1013. }
  1014. }
  1015. switch (cellType) {
  1016. case NUMERIC :
  1017. if (DateUtil.isCellDateFormatted(cell, cfEvaluator)) {
  1018. return getFormattedDateString(cell, cfEvaluator);
  1019. }
  1020. return getFormattedNumberString(cell, cfEvaluator);
  1021. case STRING :
  1022. return cell.getRichStringCellValue().getString();
  1023. case BOOLEAN :
  1024. return cell.getBooleanCellValue() ? "TRUE" : "FALSE";
  1025. case BLANK :
  1026. return "";
  1027. case ERROR:
  1028. return FormulaError.forInt(cell.getErrorCellValue()).getString();
  1029. default:
  1030. throw new IllegalStateException("Unexpected celltype (" + cellType + ")");
  1031. }
  1032. }
  1033. /**
  1034. * <p>
  1035. * Sets a default number format to be used when the Excel format cannot be
  1036. * parsed successfully. <b>Note:</b> This is a fall back for when an error
  1037. * occurs while parsing an Excel number format pattern. This will not
  1038. * affect cells with the <em>General</em> format.
  1039. * </p>
  1040. * <p>
  1041. * The value that will be passed to the Format's format method (specified
  1042. * by {@code java.text.Format#format}) will be a double value from a
  1043. * numeric cell. Therefore the code in the format method should expect a
  1044. * {@code Number} value.
  1045. * </p>
  1046. *
  1047. * @param format A Format instance to be used as a default
  1048. * @see Format#format
  1049. */
  1050. public void setDefaultNumberFormat(Format format) {
  1051. for (Map.Entry<String, Format> entry : formats.entrySet()) {
  1052. if (entry.getValue() == generalNumberFormat) {
  1053. entry.setValue(format);
  1054. }
  1055. }
  1056. defaultNumFormat = format;
  1057. }
  1058. /**
  1059. * Adds a new format to the available formats.
  1060. * <p>
  1061. * The value that will be passed to the Format's format method (specified
  1062. * by {@code java.text.Format#format}) will be a double value from a
  1063. * numeric cell. Therefore the code in the format method should expect a
  1064. * {@code Number} value.
  1065. * </p>
  1066. * @param excelFormatStr The data format string
  1067. * @param format A Format instance
  1068. */
  1069. public void addFormat(String excelFormatStr, Format format) {
  1070. formats.put(excelFormatStr, format);
  1071. }
  1072. // Some custom formats
  1073. /**
  1074. * @return a {@code DecimalFormat} with parseIntegerOnly set {@code true}
  1075. */
  1076. private static DecimalFormat createIntegerOnlyFormat(String fmt) {
  1077. DecimalFormatSymbols dsf = DecimalFormatSymbols.getInstance(Locale.ROOT);
  1078. DecimalFormat result = new DecimalFormat(fmt, dsf);
  1079. result.setParseIntegerOnly(true);
  1080. return result;
  1081. }
  1082. /**
  1083. * Enables excel style rounding mode (round half up) on the
  1084. * Decimal Format given.
  1085. */
  1086. public static void setExcelStyleRoundingMode(DecimalFormat format) {
  1087. setExcelStyleRoundingMode(format, RoundingMode.HALF_UP);
  1088. }
  1089. /**
  1090. * Enables custom rounding mode on the given Decimal Format.
  1091. * @param format DecimalFormat
  1092. * @param roundingMode RoundingMode
  1093. */
  1094. public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) {
  1095. format.setRoundingMode(roundingMode);
  1096. }
  1097. /**
  1098. * If the Locale has been changed via {@link LocaleUtil#setUserLocale(Locale)} the stored
  1099. * formats need to be refreshed. All formats which aren't originated from DataFormatter
  1100. * itself, i.e. all Formats added via {@link DataFormatter#addFormat(String, Format)} and
  1101. * {@link DataFormatter#setDefaultNumberFormat(Format)}, need to be added again.
  1102. * To notify callers, the returned {@link PropertyChangeSupport} should be used.
  1103. * The Locale in {@link #updateLocale(Locale)} is the new Locale.
  1104. *
  1105. * @return the listener object, where callers can register themselves
  1106. */
  1107. public PropertyChangeSupport getLocaleChangedObservable() {
  1108. return pcs;
  1109. }
  1110. private void checkForLocaleChange() {
  1111. checkForLocaleChange(LocaleUtil.getUserLocale());
  1112. }
  1113. private void checkForLocaleChange(Locale newLocale) {
  1114. if (!localeIsAdapting) return;
  1115. if (newLocale.equals(locale)) return;
  1116. updateLocale(newLocale);
  1117. pcs.firePropertyChange("locale", locale, newLocale);
  1118. }
  1119. /**
  1120. * Update formats when locale has been changed
  1121. *
  1122. * @param newLocale the new locale
  1123. */
  1124. public void updateLocale(Locale newLocale) {
  1125. if (!localeIsAdapting || newLocale.equals(locale)) return;
  1126. locale = newLocale;
  1127. dateSymbols = DateFormatSymbols.getInstance(locale);
  1128. decimalSymbols = DecimalFormatSymbols.getInstance(locale);
  1129. generalNumberFormat = new ExcelGeneralNumberFormat(locale);
  1130. // taken from Date.toString()
  1131. defaultDateformat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", dateSymbols);
  1132. defaultDateformat.setTimeZone(LocaleUtil.getUserTimeZone());
  1133. // init built-in formats
  1134. formats.clear();
  1135. Format zipFormat = ZipPlusFourFormat.instance;
  1136. addFormat("00000\\-0000", zipFormat);
  1137. addFormat("00000-0000", zipFormat);
  1138. Format phoneFormat = PhoneFormat.instance;
  1139. // allow for format string variations
  1140. addFormat("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
  1141. addFormat("[<=9999999]###-####;(###) ###-####", phoneFormat);
  1142. addFormat("###\\-####;\\(###\\)\\ ###\\-####", phoneFormat);
  1143. addFormat("###-####;(###) ###-####", phoneFormat);
  1144. Format ssnFormat = SSNFormat.instance;
  1145. addFormat("000\\-00\\-0000", ssnFormat);
  1146. addFormat("000-00-0000", ssnFormat);
  1147. }
  1148. /**
  1149. * Format class for Excel's SSN format. This class mimics Excel's built-in
  1150. * SSN formatting.
  1151. */
  1152. @SuppressWarnings("serial")
  1153. private static final class SSNFormat extends Format {
  1154. public static final Format instance = new SSNFormat();
  1155. private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
  1156. private SSNFormat() {
  1157. // enforce singleton
  1158. }
  1159. /** Format a number as an SSN */
  1160. public static String format(Number num) {
  1161. String result = df.format(num);
  1162. return result.substring(0, 3) + '-' +
  1163. result.substring(3, 5) + '-' +
  1164. result.substring(5, 9);
  1165. }
  1166. @Override
  1167. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  1168. return toAppendTo.append(format((Number)obj));
  1169. }
  1170. @Override
  1171. public Object parseObject(String source, ParsePosition pos) {
  1172. return df.parseObject(source, pos);
  1173. }
  1174. }
  1175. /**
  1176. * Format class for Excel Zip + 4 format. This class mimics Excel's
  1177. * built-in formatting for Zip + 4.
  1178. */
  1179. @SuppressWarnings("serial")
  1180. private static final class ZipPlusFourFormat extends Format {
  1181. public static final Format instance = new ZipPlusFourFormat();
  1182. private static final DecimalFormat df = createIntegerOnlyFormat("000000000");
  1183. private ZipPlusFourFormat() {
  1184. // enforce singleton
  1185. }
  1186. /** Format a number as Zip + 4 */
  1187. public static String format(Number num) {
  1188. String result = df.format(num);
  1189. return result.substring(0, 5) + '-' +
  1190. result.substring(5, 9);
  1191. }
  1192. @Override
  1193. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  1194. return toAppendTo.append(format((Number)obj));
  1195. }
  1196. @Override
  1197. public Object parseObject(String source, ParsePosition pos) {
  1198. return df.parseObject(source, pos);
  1199. }
  1200. }
  1201. /**
  1202. * Format class for Excel phone number format. This class mimics Excel's
  1203. * built-in phone number formatting.
  1204. */
  1205. @SuppressWarnings("serial")
  1206. private static final class PhoneFormat extends Format {
  1207. public static final Format instance = new PhoneFormat();
  1208. private static final DecimalFormat df = createIntegerOnlyFormat("##########");
  1209. private PhoneFormat() {
  1210. // enforce singleton
  1211. }
  1212. /** Format a number as a phone number */
  1213. public static String format(Number num) {
  1214. String result = df.format(num);
  1215. StringBuilder sb = new StringBuilder();
  1216. String seg1, seg2, seg3;
  1217. int len = result.length();
  1218. if (len <= 4) {
  1219. return result;
  1220. }
  1221. seg3 = result.substring(len - 4, len);
  1222. seg2 = result.substring(Math.max(0, len - 7), len - 4);
  1223. seg1 = result.substring(Math.max(0, len - 10), Math.max(0, len - 7));
  1224. if(StringUtil.isNotBlank(seg1)) {
  1225. sb.append('(').append(seg1).append(") ");
  1226. }
  1227. if(StringUtil.isNotBlank(seg2)) {
  1228. sb.append(seg2).append('-');
  1229. }
  1230. sb.append(seg3);
  1231. return sb.toString();
  1232. }
  1233. @Override
  1234. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  1235. return toAppendTo.append(format((Number)obj));
  1236. }
  1237. @Override
  1238. public Object parseObject(String source, ParsePosition pos) {
  1239. return df.parseObject(source, pos);
  1240. }
  1241. }
  1242. /**
  1243. * Format class that does nothing and always returns a constant string.
  1244. *
  1245. * This format is used to simulate Excel's handling of a format string
  1246. * of all # when the value is 0. Excel will output "", Java will output "0".
  1247. *
  1248. * @see DataFormatter#createFormat(double, int, String)
  1249. */
  1250. @SuppressWarnings("serial")
  1251. private static final class ConstantStringFormat extends Format {
  1252. private static final DecimalFormat df = createIntegerOnlyFormat("##########");
  1253. private final String str;
  1254. public ConstantStringFormat(String s) {
  1255. str = s;
  1256. }
  1257. @Override
  1258. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  1259. return toAppendTo.append(str);
  1260. }
  1261. @Override
  1262. public Object parseObject(String source, ParsePosition pos) {
  1263. return df.parseObject(source, pos);
  1264. }
  1265. }
  1266. /**
  1267. * Workaround until we merge {@link DataFormatter} with {@link CellFormat}.
  1268. * Constant, non-cachable wrapper around a {@link CellFormatResult}
  1269. */
  1270. @SuppressWarnings("serial")
  1271. private final class CellFormatResultWrapper extends Format {
  1272. private final CellFormatResult result;
  1273. private CellFormatResultWrapper(CellFormatResult result) {
  1274. this.result = result;
  1275. }
  1276. @Override
  1277. public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
  1278. if (emulateCSV) {
  1279. return toAppendTo.append(result.text);
  1280. } else {
  1281. return toAppendTo.append(result.text.trim());
  1282. }
  1283. }
  1284. @Override
  1285. public Object parseObject(String source, ParsePosition pos) {
  1286. return null; // Not supported
  1287. }
  1288. }
  1289. }