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.

CellFormat.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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.format;
  16. import org.apache.poi.ss.usermodel.Cell;
  17. import javax.swing.*;
  18. import java.util.ArrayList;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.WeakHashMap;
  22. import java.util.logging.Level;
  23. import java.util.regex.Matcher;
  24. import java.util.regex.Pattern;
  25. /**
  26. * Format a value according to the standard Excel behavior. This "standard" is
  27. * not explicitly documented by Microsoft, so the behavior is determined by
  28. * experimentation; see the tests.
  29. * <p/>
  30. * An Excel format has up to four parts, separated by semicolons. Each part
  31. * specifies what to do with particular kinds of values, depending on the number
  32. * of parts given: <dl> <dt>One part (example: <tt>[Green]#.##</tt>) <dd>If the
  33. * value is a number, display according to this one part (example: green text,
  34. * with up to two decimal points). If the value is text, display it as is.
  35. * <dt>Two parts (example: <tt>[Green]#.##;[Red]#.##</tt>) <dd>If the value is a
  36. * positive number or zero, display according to the first part (example: green
  37. * text, with up to two decimal points); if it is a negative number, display
  38. * according to the second part (example: red text, with up to two decimal
  39. * points). If the value is text, display it as is. <dt>Three parts (example:
  40. * <tt>[Green]#.##;[Black]#.##;[Red]#.##</tt>) <dd>If the value is a positive
  41. * number, display according to the first part (example: green text, with up to
  42. * two decimal points); if it is zero, display according to the second part
  43. * (example: black text, with up to two decimal points); if it is a negative
  44. * number, display according to the third part (example: red text, with up to
  45. * two decimal points). If the value is text, display it as is. <dt>Four parts
  46. * (example: <tt>[Green]#.##;[Black]#.##;[Red]#.##;[@]</tt>) <dd>If the value is
  47. * a positive number, display according to the first part (example: green text,
  48. * with up to two decimal points); if it is zero, display according to the
  49. * second part (example: black text, with up to two decimal points); if it is a
  50. * negative number, display according to the third part (example: red text, with
  51. * up to two decimal points). If the value is text, display according to the
  52. * fourth part (example: text in the cell's usual color, with the text value
  53. * surround by brackets). </dl>
  54. * <p/>
  55. * In addition to these, there is a general format that is used when no format
  56. * is specified. This formatting is presented by the {@link #GENERAL_FORMAT}
  57. * object.
  58. *
  59. * @author Ken Arnold, Industrious Media LLC
  60. */
  61. @SuppressWarnings({"Singleton"})
  62. public class CellFormat {
  63. private final String format;
  64. private final CellFormatPart posNumFmt;
  65. private final CellFormatPart zeroNumFmt;
  66. private final CellFormatPart negNumFmt;
  67. private final CellFormatPart textFmt;
  68. private static final Pattern ONE_PART = Pattern.compile(
  69. CellFormatPart.FORMAT_PAT.pattern() + "(;|$)",
  70. Pattern.COMMENTS | Pattern.CASE_INSENSITIVE);
  71. private static final CellFormatPart DEFAULT_TEXT_FORMAT =
  72. new CellFormatPart("@");
  73. /**
  74. * Format a value as it would be were no format specified. This is also
  75. * used when the format specified is <tt>General</tt>.
  76. */
  77. public static final CellFormat GENERAL_FORMAT = new CellFormat("General") {
  78. @Override
  79. public CellFormatResult apply(Object value) {
  80. String text;
  81. if (value == null) {
  82. text = "";
  83. } else if (value instanceof Number) {
  84. text = CellNumberFormatter.SIMPLE_NUMBER.format(value);
  85. } else {
  86. text = value.toString();
  87. }
  88. return new CellFormatResult(true, text, null);
  89. }
  90. };
  91. /** Maps a format string to its parsed version for efficiencies sake. */
  92. private static final Map<String, CellFormat> formatCache =
  93. new WeakHashMap<String, CellFormat>();
  94. /**
  95. * Returns a {@link CellFormat} that applies the given format. Two calls
  96. * with the same format may or may not return the same object.
  97. *
  98. * @param format The format.
  99. *
  100. * @return A {@link CellFormat} that applies the given format.
  101. */
  102. public static CellFormat getInstance(String format) {
  103. CellFormat fmt = formatCache.get(format);
  104. if (fmt == null) {
  105. if (format.equals("General"))
  106. fmt = GENERAL_FORMAT;
  107. else
  108. fmt = new CellFormat(format);
  109. formatCache.put(format, fmt);
  110. }
  111. return fmt;
  112. }
  113. /**
  114. * Creates a new object.
  115. *
  116. * @param format The format.
  117. */
  118. private CellFormat(String format) {
  119. this.format = format;
  120. Matcher m = ONE_PART.matcher(format);
  121. List<CellFormatPart> parts = new ArrayList<CellFormatPart>();
  122. while (m.find()) {
  123. try {
  124. String valueDesc = m.group();
  125. // Strip out the semicolon if it's there
  126. if (valueDesc.endsWith(";"))
  127. valueDesc = valueDesc.substring(0, valueDesc.length() - 1);
  128. parts.add(new CellFormatPart(valueDesc));
  129. } catch (RuntimeException e) {
  130. CellFormatter.logger.log(Level.WARNING,
  131. "Invalid format: " + CellFormatter.quote(m.group()), e);
  132. parts.add(null);
  133. }
  134. }
  135. switch (parts.size()) {
  136. case 1:
  137. posNumFmt = zeroNumFmt = negNumFmt = parts.get(0);
  138. textFmt = DEFAULT_TEXT_FORMAT;
  139. break;
  140. case 2:
  141. posNumFmt = zeroNumFmt = parts.get(0);
  142. negNumFmt = parts.get(1);
  143. textFmt = DEFAULT_TEXT_FORMAT;
  144. break;
  145. case 3:
  146. posNumFmt = parts.get(0);
  147. zeroNumFmt = parts.get(1);
  148. negNumFmt = parts.get(2);
  149. textFmt = DEFAULT_TEXT_FORMAT;
  150. break;
  151. case 4:
  152. default:
  153. posNumFmt = parts.get(0);
  154. zeroNumFmt = parts.get(1);
  155. negNumFmt = parts.get(2);
  156. textFmt = parts.get(3);
  157. break;
  158. }
  159. }
  160. /**
  161. * Returns the result of applying the format to the given value. If the
  162. * value is a number (a type of {@link Number} object), the correct number
  163. * format type is chosen; otherwise it is considered a text object.
  164. *
  165. * @param value The value
  166. *
  167. * @return The result, in a {@link CellFormatResult}.
  168. */
  169. public CellFormatResult apply(Object value) {
  170. if (value instanceof Number) {
  171. Number num = (Number) value;
  172. double val = num.doubleValue();
  173. if (val > 0)
  174. return posNumFmt.apply(value);
  175. else if (val < 0)
  176. return negNumFmt.apply(-val);
  177. else
  178. return zeroNumFmt.apply(value);
  179. } else {
  180. return textFmt.apply(value);
  181. }
  182. }
  183. /**
  184. * Fetches the appropriate value from the cell, and returns the result of
  185. * applying it to the appropriate format. For formula cells, the computed
  186. * value is what is used.
  187. *
  188. * @param c The cell.
  189. *
  190. * @return The result, in a {@link CellFormatResult}.
  191. */
  192. public CellFormatResult apply(Cell c) {
  193. switch (ultimateType(c)) {
  194. case Cell.CELL_TYPE_BLANK:
  195. return apply("");
  196. case Cell.CELL_TYPE_BOOLEAN:
  197. return apply(c.getStringCellValue());
  198. case Cell.CELL_TYPE_NUMERIC:
  199. return apply(c.getNumericCellValue());
  200. case Cell.CELL_TYPE_STRING:
  201. return apply(c.getStringCellValue());
  202. default:
  203. return apply("?");
  204. }
  205. }
  206. /**
  207. * Uses the result of applying this format to the value, setting the text
  208. * and color of a label before returning the result.
  209. *
  210. * @param label The label to apply to.
  211. * @param value The value to process.
  212. *
  213. * @return The result, in a {@link CellFormatResult}.
  214. */
  215. public CellFormatResult apply(JLabel label, Object value) {
  216. CellFormatResult result = apply(value);
  217. label.setText(result.text);
  218. if (result.textColor != null) {
  219. label.setForeground(result.textColor);
  220. }
  221. return result;
  222. }
  223. /**
  224. * Fetches the appropriate value from the cell, and uses the result, setting
  225. * the text and color of a label before returning the result.
  226. *
  227. * @param label The label to apply to.
  228. * @param c The cell.
  229. *
  230. * @return The result, in a {@link CellFormatResult}.
  231. */
  232. public CellFormatResult apply(JLabel label, Cell c) {
  233. switch (ultimateType(c)) {
  234. case Cell.CELL_TYPE_BLANK:
  235. return apply(label, "");
  236. case Cell.CELL_TYPE_BOOLEAN:
  237. return apply(label, c.getStringCellValue());
  238. case Cell.CELL_TYPE_NUMERIC:
  239. return apply(label, c.getNumericCellValue());
  240. case Cell.CELL_TYPE_STRING:
  241. return apply(label, c.getStringCellValue());
  242. default:
  243. return apply(label, "?");
  244. }
  245. }
  246. /**
  247. * Returns the ultimate cell type, following the results of formulas. If
  248. * the cell is a {@link Cell#CELL_TYPE_FORMULA}, this returns the result of
  249. * {@link Cell#getCachedFormulaResultType()}. Otherwise this returns the
  250. * result of {@link Cell#getCellType()}.
  251. *
  252. * @param cell The cell.
  253. *
  254. * @return The ultimate type of this cell.
  255. */
  256. public static int ultimateType(Cell cell) {
  257. int type = cell.getCellType();
  258. if (type == Cell.CELL_TYPE_FORMULA)
  259. return cell.getCachedFormulaResultType();
  260. else
  261. return type;
  262. }
  263. /**
  264. * Returns <tt>true</tt> if the other object is a {@link CellFormat} object
  265. * with the same format.
  266. *
  267. * @param obj The other object.
  268. *
  269. * @return <tt>true</tt> if the two objects are equal.
  270. */
  271. @Override
  272. public boolean equals(Object obj) {
  273. if (this == obj)
  274. return true;
  275. if (obj instanceof CellFormat) {
  276. CellFormat that = (CellFormat) obj;
  277. return format.equals(that.format);
  278. }
  279. return false;
  280. }
  281. /**
  282. * Returns a hash code for the format.
  283. *
  284. * @return A hash code for the format.
  285. */
  286. @Override
  287. public int hashCode() {
  288. return format.hashCode();
  289. }
  290. }