您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

CellNumberFormatter.java 39KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085
  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.format.CellFormatPart.PartHandler;
  17. import java.text.DecimalFormat;
  18. import java.text.FieldPosition;
  19. import java.util.BitSet;
  20. import java.util.Collections;
  21. import java.util.Formatter;
  22. import java.util.Iterator;
  23. import java.util.LinkedList;
  24. import java.util.List;
  25. import java.util.ListIterator;
  26. import java.util.Set;
  27. import java.util.TreeSet;
  28. import java.util.regex.Matcher;
  29. /**
  30. * This class implements printing out a value using a number format.
  31. *
  32. * @author Ken Arnold, Industrious Media LLC
  33. */
  34. public class CellNumberFormatter extends CellFormatter {
  35. private final String desc;
  36. private String printfFmt;
  37. private double scale;
  38. private Special decimalPoint;
  39. private Special slash;
  40. private Special exponent;
  41. private Special numerator;
  42. private Special afterInteger;
  43. private Special afterFractional;
  44. private boolean integerCommas;
  45. private final List<Special> specials;
  46. private List<Special> integerSpecials;
  47. private List<Special> fractionalSpecials;
  48. private List<Special> numeratorSpecials;
  49. private List<Special> denominatorSpecials;
  50. private List<Special> exponentSpecials;
  51. private List<Special> exponentDigitSpecials;
  52. private int maxDenominator;
  53. private String numeratorFmt;
  54. private String denominatorFmt;
  55. private boolean improperFraction;
  56. private DecimalFormat decimalFmt;
  57. static final CellFormatter SIMPLE_NUMBER = new CellFormatter("General") {
  58. public void formatValue(StringBuffer toAppendTo, Object value) {
  59. if (value == null)
  60. return;
  61. if (value instanceof Number) {
  62. Number num = (Number) value;
  63. if (num.doubleValue() % 1.0 == 0)
  64. SIMPLE_INT.formatValue(toAppendTo, value);
  65. else
  66. SIMPLE_FLOAT.formatValue(toAppendTo, value);
  67. } else {
  68. CellTextFormatter.SIMPLE_TEXT.formatValue(toAppendTo, value);
  69. }
  70. }
  71. public void simpleValue(StringBuffer toAppendTo, Object value) {
  72. formatValue(toAppendTo, value);
  73. }
  74. };
  75. private static final CellFormatter SIMPLE_INT = new CellNumberFormatter(
  76. "#");
  77. private static final CellFormatter SIMPLE_FLOAT = new CellNumberFormatter(
  78. "#.#");
  79. /**
  80. * This class is used to mark where the special characters in the format
  81. * are, as opposed to the other characters that are simply printed.
  82. */
  83. static class Special {
  84. final char ch;
  85. int pos;
  86. Special(char ch, int pos) {
  87. this.ch = ch;
  88. this.pos = pos;
  89. }
  90. @Override
  91. public String toString() {
  92. return "'" + ch + "' @ " + pos;
  93. }
  94. }
  95. /**
  96. * This class represents a single modification to a result string. The way
  97. * this works is complicated, but so is numeric formatting. In general, for
  98. * most formats, we use a DecimalFormat object that will put the string out
  99. * in a known format, usually with all possible leading and trailing zeros.
  100. * We then walk through the result and the orginal format, and note any
  101. * modifications that need to be made. Finally, we go through and apply
  102. * them all, dealing with overlapping modifications.
  103. */
  104. static class StringMod implements Comparable<StringMod> {
  105. final Special special;
  106. final int op;
  107. CharSequence toAdd;
  108. Special end;
  109. boolean startInclusive;
  110. boolean endInclusive;
  111. public static final int BEFORE = 1;
  112. public static final int AFTER = 2;
  113. public static final int REPLACE = 3;
  114. private StringMod(Special special, CharSequence toAdd, int op) {
  115. this.special = special;
  116. this.toAdd = toAdd;
  117. this.op = op;
  118. }
  119. public StringMod(Special start, boolean startInclusive, Special end,
  120. boolean endInclusive, char toAdd) {
  121. this(start, startInclusive, end, endInclusive);
  122. this.toAdd = toAdd + "";
  123. }
  124. public StringMod(Special start, boolean startInclusive, Special end,
  125. boolean endInclusive) {
  126. special = start;
  127. this.startInclusive = startInclusive;
  128. this.end = end;
  129. this.endInclusive = endInclusive;
  130. op = REPLACE;
  131. toAdd = "";
  132. }
  133. public int compareTo(StringMod that) {
  134. int diff = special.pos - that.special.pos;
  135. if (diff != 0)
  136. return diff;
  137. else
  138. return op - that.op;
  139. }
  140. @Override
  141. public boolean equals(Object that) {
  142. try {
  143. return compareTo((StringMod) that) == 0;
  144. } catch (RuntimeException ignored) {
  145. // NullPointerException or CastException
  146. return false;
  147. }
  148. }
  149. @Override
  150. public int hashCode() {
  151. return special.hashCode() + op;
  152. }
  153. }
  154. private class NumPartHandler implements PartHandler {
  155. private char insertSignForExponent;
  156. public String handlePart(Matcher m, String part, CellFormatType type,
  157. StringBuffer desc) {
  158. int pos = desc.length();
  159. char firstCh = part.charAt(0);
  160. switch (firstCh) {
  161. case 'e':
  162. case 'E':
  163. // See comment in writeScientific -- exponent handling is complex.
  164. // (1) When parsing the format, remove the sign from after the 'e' and
  165. // put it before the first digit of the exponent.
  166. if (exponent == null && specials.size() > 0) {
  167. specials.add(exponent = new Special('.', pos));
  168. insertSignForExponent = part.charAt(1);
  169. return part.substring(0, 1);
  170. }
  171. break;
  172. case '0':
  173. case '?':
  174. case '#':
  175. if (insertSignForExponent != '\0') {
  176. specials.add(new Special(insertSignForExponent, pos));
  177. desc.append(insertSignForExponent);
  178. insertSignForExponent = '\0';
  179. pos++;
  180. }
  181. for (int i = 0; i < part.length(); i++) {
  182. char ch = part.charAt(i);
  183. specials.add(new Special(ch, pos + i));
  184. }
  185. break;
  186. case '.':
  187. if (decimalPoint == null && specials.size() > 0)
  188. specials.add(decimalPoint = new Special('.', pos));
  189. break;
  190. case '/':
  191. //!! This assumes there is a numerator and a denominator, but these are actually optional
  192. if (slash == null && specials.size() > 0) {
  193. numerator = previousNumber();
  194. // If the first number in the whole format is the numerator, the
  195. // entire number should be printed as an improper fraction
  196. if (numerator == firstDigit(specials))
  197. improperFraction = true;
  198. specials.add(slash = new Special('.', pos));
  199. }
  200. break;
  201. case '%':
  202. // don't need to remember because we don't need to do anything with these
  203. scale *= 100;
  204. break;
  205. default:
  206. return null;
  207. }
  208. return part;
  209. }
  210. }
  211. /**
  212. * Creates a new cell number formatter.
  213. *
  214. * @param format The format to parse.
  215. */
  216. public CellNumberFormatter(String format) {
  217. super(format);
  218. scale = 1;
  219. specials = new LinkedList<Special>();
  220. NumPartHandler partHandler = new NumPartHandler();
  221. StringBuffer descBuf = CellFormatPart.parseFormat(format,
  222. CellFormatType.NUMBER, partHandler);
  223. // These are inconsistent settings, so ditch 'em
  224. if ((decimalPoint != null || exponent != null) && slash != null) {
  225. slash = null;
  226. numerator = null;
  227. }
  228. interpretCommas(descBuf);
  229. int precision;
  230. int fractionPartWidth = 0;
  231. if (decimalPoint == null) {
  232. precision = 0;
  233. } else {
  234. precision = interpretPrecision();
  235. fractionPartWidth = 1 + precision;
  236. if (precision == 0) {
  237. // This means the format has a ".", but that output should have no decimals after it.
  238. // We just stop treating it specially
  239. specials.remove(decimalPoint);
  240. decimalPoint = null;
  241. }
  242. }
  243. if (precision == 0)
  244. fractionalSpecials = Collections.emptyList();
  245. else
  246. fractionalSpecials = specials.subList(specials.indexOf(
  247. decimalPoint) + 1, fractionalEnd());
  248. if (exponent == null)
  249. exponentSpecials = Collections.emptyList();
  250. else {
  251. int exponentPos = specials.indexOf(exponent);
  252. exponentSpecials = specialsFor(exponentPos, 2);
  253. exponentDigitSpecials = specialsFor(exponentPos + 2);
  254. }
  255. if (slash == null) {
  256. numeratorSpecials = Collections.emptyList();
  257. denominatorSpecials = Collections.emptyList();
  258. } else {
  259. if (numerator == null)
  260. numeratorSpecials = Collections.emptyList();
  261. else
  262. numeratorSpecials = specialsFor(specials.indexOf(numerator));
  263. denominatorSpecials = specialsFor(specials.indexOf(slash) + 1);
  264. if (denominatorSpecials.isEmpty()) {
  265. // no denominator follows the slash, drop the fraction idea
  266. numeratorSpecials = Collections.emptyList();
  267. } else {
  268. maxDenominator = maxValue(denominatorSpecials);
  269. numeratorFmt = singleNumberFormat(numeratorSpecials);
  270. denominatorFmt = singleNumberFormat(denominatorSpecials);
  271. }
  272. }
  273. integerSpecials = specials.subList(0, integerEnd());
  274. if (exponent == null) {
  275. StringBuffer fmtBuf = new StringBuffer("%");
  276. int integerPartWidth = calculateIntegerPartWidth();
  277. int totalWidth = integerPartWidth + fractionPartWidth;
  278. fmtBuf.append('0').append(totalWidth).append('.').append(precision);
  279. fmtBuf.append("f");
  280. printfFmt = fmtBuf.toString();
  281. } else {
  282. StringBuffer fmtBuf = new StringBuffer();
  283. boolean first = true;
  284. List<Special> specialList = integerSpecials;
  285. if (integerSpecials.size() == 1) {
  286. // If we don't do this, we get ".6e5" instead of "6e4"
  287. fmtBuf.append("0");
  288. first = false;
  289. } else
  290. for (Special s : specialList) {
  291. if (isDigitFmt(s)) {
  292. fmtBuf.append(first ? '#' : '0');
  293. first = false;
  294. }
  295. }
  296. if (fractionalSpecials.size() > 0) {
  297. fmtBuf.append('.');
  298. for (Special s : fractionalSpecials) {
  299. if (isDigitFmt(s)) {
  300. if (!first)
  301. fmtBuf.append('0');
  302. first = false;
  303. }
  304. }
  305. }
  306. fmtBuf.append('E');
  307. placeZeros(fmtBuf, exponentSpecials.subList(2,
  308. exponentSpecials.size()));
  309. decimalFmt = new DecimalFormat(fmtBuf.toString());
  310. }
  311. if (exponent != null)
  312. scale =
  313. 1; // in "e" formats,% and trailing commas have no scaling effect
  314. desc = descBuf.toString();
  315. }
  316. private static void placeZeros(StringBuffer sb, List<Special> specials) {
  317. for (Special s : specials) {
  318. if (isDigitFmt(s))
  319. sb.append('0');
  320. }
  321. }
  322. private static Special firstDigit(List<Special> specials) {
  323. for (Special s : specials) {
  324. if (isDigitFmt(s))
  325. return s;
  326. }
  327. return null;
  328. }
  329. static StringMod insertMod(Special special, CharSequence toAdd, int where) {
  330. return new StringMod(special, toAdd, where);
  331. }
  332. static StringMod deleteMod(Special start, boolean startInclusive,
  333. Special end, boolean endInclusive) {
  334. return new StringMod(start, startInclusive, end, endInclusive);
  335. }
  336. static StringMod replaceMod(Special start, boolean startInclusive,
  337. Special end, boolean endInclusive, char withChar) {
  338. return new StringMod(start, startInclusive, end, endInclusive,
  339. withChar);
  340. }
  341. private static String singleNumberFormat(List<Special> numSpecials) {
  342. return "%0" + numSpecials.size() + "d";
  343. }
  344. private static int maxValue(List<Special> s) {
  345. return (int) Math.round(Math.pow(10, s.size()) - 1);
  346. }
  347. private List<Special> specialsFor(int pos, int takeFirst) {
  348. if (pos >= specials.size())
  349. return Collections.emptyList();
  350. ListIterator<Special> it = specials.listIterator(pos + takeFirst);
  351. Special last = it.next();
  352. int end = pos + takeFirst;
  353. while (it.hasNext()) {
  354. Special s = it.next();
  355. if (!isDigitFmt(s) || s.pos - last.pos > 1)
  356. break;
  357. end++;
  358. last = s;
  359. }
  360. return specials.subList(pos, end + 1);
  361. }
  362. private List<Special> specialsFor(int pos) {
  363. return specialsFor(pos, 0);
  364. }
  365. private static boolean isDigitFmt(Special s) {
  366. return s.ch == '0' || s.ch == '?' || s.ch == '#';
  367. }
  368. private Special previousNumber() {
  369. ListIterator<Special> it = specials.listIterator(specials.size());
  370. while (it.hasPrevious()) {
  371. Special s = it.previous();
  372. if (isDigitFmt(s)) {
  373. Special numStart = s;
  374. Special last = s;
  375. while (it.hasPrevious()) {
  376. s = it.previous();
  377. if (last.pos - s.pos > 1) // it has to be continuous digits
  378. break;
  379. if (isDigitFmt(s))
  380. numStart = s;
  381. else
  382. break;
  383. last = s;
  384. }
  385. return numStart;
  386. }
  387. }
  388. return null;
  389. }
  390. private int calculateIntegerPartWidth() {
  391. ListIterator<Special> it = specials.listIterator();
  392. int digitCount = 0;
  393. while (it.hasNext()) {
  394. Special s = it.next();
  395. //!! Handle fractions: The previous set of digits before that is the numerator, so we should stop short of that
  396. if (s == afterInteger)
  397. break;
  398. else if (isDigitFmt(s))
  399. digitCount++;
  400. }
  401. return digitCount;
  402. }
  403. private int interpretPrecision() {
  404. if (decimalPoint == null) {
  405. return -1;
  406. } else {
  407. int precision = 0;
  408. ListIterator<Special> it = specials.listIterator(specials.indexOf(
  409. decimalPoint));
  410. if (it.hasNext())
  411. it.next(); // skip over the decimal point itself
  412. while (it.hasNext()) {
  413. Special s = it.next();
  414. if (isDigitFmt(s))
  415. precision++;
  416. else
  417. break;
  418. }
  419. return precision;
  420. }
  421. }
  422. private void interpretCommas(StringBuffer sb) {
  423. // In the integer part, commas at the end are scaling commas; other commas mean to show thousand-grouping commas
  424. ListIterator<Special> it = specials.listIterator(integerEnd());
  425. boolean stillScaling = true;
  426. integerCommas = false;
  427. while (it.hasPrevious()) {
  428. Special s = it.previous();
  429. if (s.ch != ',') {
  430. stillScaling = false;
  431. } else {
  432. if (stillScaling) {
  433. scale /= 1000;
  434. } else {
  435. integerCommas = true;
  436. }
  437. }
  438. }
  439. if (decimalPoint != null) {
  440. it = specials.listIterator(fractionalEnd());
  441. while (it.hasPrevious()) {
  442. Special s = it.previous();
  443. if (s.ch != ',') {
  444. break;
  445. } else {
  446. scale /= 1000;
  447. }
  448. }
  449. }
  450. // Now strip them out -- we only need their interpretation, not their presence
  451. it = specials.listIterator();
  452. int removed = 0;
  453. while (it.hasNext()) {
  454. Special s = it.next();
  455. s.pos -= removed;
  456. if (s.ch == ',') {
  457. removed++;
  458. it.remove();
  459. sb.deleteCharAt(s.pos);
  460. }
  461. }
  462. }
  463. private int integerEnd() {
  464. if (decimalPoint != null)
  465. afterInteger = decimalPoint;
  466. else if (exponent != null)
  467. afterInteger = exponent;
  468. else if (numerator != null)
  469. afterInteger = numerator;
  470. else
  471. afterInteger = null;
  472. return afterInteger == null ? specials.size() : specials.indexOf(
  473. afterInteger);
  474. }
  475. private int fractionalEnd() {
  476. int end;
  477. if (exponent != null)
  478. afterFractional = exponent;
  479. else if (numerator != null)
  480. afterInteger = numerator;
  481. else
  482. afterFractional = null;
  483. end = afterFractional == null ? specials.size() : specials.indexOf(
  484. afterFractional);
  485. return end;
  486. }
  487. /** {@inheritDoc} */
  488. public void formatValue(StringBuffer toAppendTo, Object valueObject) {
  489. double value = ((Number) valueObject).doubleValue();
  490. value *= scale;
  491. // the '-' sign goes at the front, always, so we pick it out
  492. boolean negative = value < 0;
  493. if (negative)
  494. value = -value;
  495. // Split out the fractional part if we need to print a fraction
  496. double fractional = 0;
  497. if (slash != null) {
  498. if (improperFraction) {
  499. fractional = value;
  500. value = 0;
  501. } else {
  502. fractional = value % 1.0;
  503. //noinspection SillyAssignment
  504. value = (long) value;
  505. }
  506. }
  507. Set<StringMod> mods = new TreeSet<StringMod>();
  508. StringBuffer output = new StringBuffer(desc);
  509. if (exponent != null) {
  510. writeScientific(value, output, mods);
  511. } else if (improperFraction) {
  512. writeFraction(value, null, fractional, output, mods);
  513. } else {
  514. StringBuffer result = new StringBuffer();
  515. Formatter f = new Formatter(result);
  516. f.format(LOCALE, printfFmt, value);
  517. if (numerator == null) {
  518. writeFractional(result, output);
  519. writeInteger(result, output, integerSpecials, mods,
  520. integerCommas);
  521. } else {
  522. writeFraction(value, result, fractional, output, mods);
  523. }
  524. }
  525. // Now strip out any remaining '#'s and add any pending text ...
  526. ListIterator<Special> it = specials.listIterator();
  527. Iterator<StringMod> changes = mods.iterator();
  528. StringMod nextChange = (changes.hasNext() ? changes.next() : null);
  529. int adjust = 0;
  530. BitSet deletedChars = new BitSet(); // records chars already deleted
  531. while (it.hasNext()) {
  532. Special s = it.next();
  533. int adjustedPos = s.pos + adjust;
  534. if (!deletedChars.get(s.pos) && output.charAt(adjustedPos) == '#') {
  535. output.deleteCharAt(adjustedPos);
  536. adjust--;
  537. deletedChars.set(s.pos);
  538. }
  539. while (nextChange != null && s == nextChange.special) {
  540. int lenBefore = output.length();
  541. int modPos = s.pos + adjust;
  542. int posTweak = 0;
  543. switch (nextChange.op) {
  544. case StringMod.AFTER:
  545. // ignore adding a comma after a deleted char (which was a '#')
  546. if (nextChange.toAdd.equals(",") && deletedChars.get(s.pos))
  547. break;
  548. posTweak = 1;
  549. //noinspection fallthrough
  550. case StringMod.BEFORE:
  551. output.insert(modPos + posTweak, nextChange.toAdd);
  552. break;
  553. case StringMod.REPLACE:
  554. int delPos =
  555. s.pos; // delete starting pos in original coordinates
  556. if (!nextChange.startInclusive) {
  557. delPos++;
  558. modPos++;
  559. }
  560. // Skip over anything already deleted
  561. while (deletedChars.get(delPos)) {
  562. delPos++;
  563. modPos++;
  564. }
  565. int delEndPos =
  566. nextChange.end.pos; // delete end point in original
  567. if (nextChange.endInclusive)
  568. delEndPos++;
  569. int modEndPos =
  570. delEndPos + adjust; // delete end point in current
  571. if (modPos < modEndPos) {
  572. if (nextChange.toAdd == "")
  573. output.delete(modPos, modEndPos);
  574. else {
  575. char fillCh = nextChange.toAdd.charAt(0);
  576. for (int i = modPos; i < modEndPos; i++)
  577. output.setCharAt(i, fillCh);
  578. }
  579. deletedChars.set(delPos, delEndPos);
  580. }
  581. break;
  582. default:
  583. throw new IllegalStateException(
  584. "Unknown op: " + nextChange.op);
  585. }
  586. adjust += output.length() - lenBefore;
  587. if (changes.hasNext())
  588. nextChange = changes.next();
  589. else
  590. nextChange = null;
  591. }
  592. }
  593. // Finally, add it to the string
  594. if (negative)
  595. toAppendTo.append('-');
  596. toAppendTo.append(output);
  597. }
  598. private void writeScientific(double value, StringBuffer output,
  599. Set<StringMod> mods) {
  600. StringBuffer result = new StringBuffer();
  601. FieldPosition fractionPos = new FieldPosition(
  602. DecimalFormat.FRACTION_FIELD);
  603. decimalFmt.format(value, result, fractionPos);
  604. writeInteger(result, output, integerSpecials, mods, integerCommas);
  605. writeFractional(result, output);
  606. /*
  607. * Exponent sign handling is complex.
  608. *
  609. * In DecimalFormat, you never put the sign in the format, and the sign only
  610. * comes out of the format if it is negative.
  611. *
  612. * In Excel, you always say whether to always show the sign ("e+") or only
  613. * show negative signs ("e-").
  614. *
  615. * Also in Excel, where you put the sign in the format is NOT where it comes
  616. * out in the result. In the format, the sign goes with the "e"; in the
  617. * output it goes with the exponent value. That is, if you say "#e-|#" you
  618. * get "1e|-5", not "1e-|5". This makes sense I suppose, but it complicates
  619. * things.
  620. *
  621. * Finally, everything else in this formatting code assumes that the base of
  622. * the result is the original format, and that starting from that situation,
  623. * the indexes of the original special characters can be used to place the new
  624. * characters. As just described, this is not true for the exponent's sign.
  625. * <p/>
  626. * So here is how we handle it:
  627. *
  628. * (1) When parsing the format, remove the sign from after the 'e' and put it
  629. * before the first digit of the exponent (where it will be shown).
  630. *
  631. * (2) Determine the result's sign.
  632. *
  633. * (3) If it's missing, put the sign into the output to keep the result
  634. * lined up with the output. (In the result, "after the 'e'" and "before the
  635. * first digit" are the same because the result has no extra chars to be in
  636. * the way.)
  637. *
  638. * (4) In the output, remove the sign if it should not be shown ("e-" was used
  639. * and the sign is negative) or set it to the correct value.
  640. */
  641. // (2) Determine the result's sign.
  642. int ePos = fractionPos.getEndIndex();
  643. int signPos = ePos + 1;
  644. char expSignRes = result.charAt(signPos);
  645. if (expSignRes != '-') {
  646. // not a sign, so it's a digit, and therefore a positive exponent
  647. expSignRes = '+';
  648. // (3) If it's missing, put the sign into the output to keep the result
  649. // lined up with the output.
  650. result.insert(signPos, '+');
  651. }
  652. // Now the result lines up like it is supposed to with the specials' indexes
  653. ListIterator<Special> it = exponentSpecials.listIterator(1);
  654. Special expSign = it.next();
  655. char expSignFmt = expSign.ch;
  656. // (4) In the output, remove the sign if it should not be shown or set it to
  657. // the correct value.
  658. if (expSignRes == '-' || expSignFmt == '+')
  659. mods.add(replaceMod(expSign, true, expSign, true, expSignRes));
  660. else
  661. mods.add(deleteMod(expSign, true, expSign, true));
  662. StringBuffer exponentNum = new StringBuffer(result.substring(
  663. signPos + 1));
  664. writeInteger(exponentNum, output, exponentDigitSpecials, mods, false);
  665. }
  666. private void writeFraction(double value, StringBuffer result,
  667. double fractional, StringBuffer output, Set<StringMod> mods) {
  668. // Figure out if we are to suppress either the integer or fractional part.
  669. // With # the suppressed part is removed; with ? it is replaced with spaces.
  670. if (!improperFraction) {
  671. // If fractional part is zero, and numerator doesn't have '0', write out
  672. // only the integer part and strip the rest.
  673. if (fractional == 0 && !hasChar('0', numeratorSpecials)) {
  674. writeInteger(result, output, integerSpecials, mods, false);
  675. Special start = integerSpecials.get(integerSpecials.size() - 1);
  676. Special end = denominatorSpecials.get(
  677. denominatorSpecials.size() - 1);
  678. if (hasChar('?', integerSpecials, numeratorSpecials,
  679. denominatorSpecials)) {
  680. //if any format has '?', then replace the fraction with spaces
  681. mods.add(replaceMod(start, false, end, true, ' '));
  682. } else {
  683. // otherwise, remove the fraction
  684. mods.add(deleteMod(start, false, end, true));
  685. }
  686. // That's all, just return
  687. return;
  688. } else {
  689. // New we check to see if we should remove the integer part
  690. boolean allZero = (value == 0 && fractional == 0);
  691. boolean willShowFraction = fractional != 0 || hasChar('0',
  692. numeratorSpecials);
  693. boolean removeBecauseZero = allZero && (hasOnly('#',
  694. integerSpecials) || !hasChar('0', numeratorSpecials));
  695. boolean removeBecauseFraction =
  696. !allZero && value == 0 && willShowFraction && !hasChar(
  697. '0', integerSpecials);
  698. if (removeBecauseZero || removeBecauseFraction) {
  699. Special start = integerSpecials.get(
  700. integerSpecials.size() - 1);
  701. if (hasChar('?', integerSpecials, numeratorSpecials)) {
  702. mods.add(replaceMod(start, true, numerator, false,
  703. ' '));
  704. } else {
  705. mods.add(deleteMod(start, true, numerator, false));
  706. }
  707. } else {
  708. // Not removing the integer part -- print it out
  709. writeInteger(result, output, integerSpecials, mods, false);
  710. }
  711. }
  712. }
  713. // Calculate and print the actual fraction (improper or otherwise)
  714. try {
  715. int n;
  716. int d;
  717. // the "fractional % 1" captures integer values in improper fractions
  718. if (fractional == 0 || (improperFraction && fractional % 1 == 0)) {
  719. // 0 as a fraction is reported by excel as 0/1
  720. n = (int) Math.round(fractional);
  721. d = 1;
  722. } else {
  723. Fraction frac = new Fraction(fractional, maxDenominator);
  724. n = frac.getNumerator();
  725. d = frac.getDenominator();
  726. }
  727. if (improperFraction)
  728. n += Math.round(value * d);
  729. writeSingleInteger(numeratorFmt, n, output, numeratorSpecials,
  730. mods);
  731. writeSingleInteger(denominatorFmt, d, output, denominatorSpecials,
  732. mods);
  733. } catch (RuntimeException ignored) {
  734. ignored.printStackTrace();
  735. }
  736. }
  737. private static boolean hasChar(char ch, List<Special>... numSpecials) {
  738. for (List<Special> specials : numSpecials) {
  739. for (Special s : specials) {
  740. if (s.ch == ch) {
  741. return true;
  742. }
  743. }
  744. }
  745. return false;
  746. }
  747. private static boolean hasOnly(char ch, List<Special>... numSpecials) {
  748. for (List<Special> specials : numSpecials) {
  749. for (Special s : specials) {
  750. if (s.ch != ch) {
  751. return false;
  752. }
  753. }
  754. }
  755. return true;
  756. }
  757. private void writeSingleInteger(String fmt, int num, StringBuffer output,
  758. List<Special> numSpecials, Set<StringMod> mods) {
  759. StringBuffer sb = new StringBuffer();
  760. Formatter formatter = new Formatter(sb);
  761. formatter.format(LOCALE, fmt, num);
  762. writeInteger(sb, output, numSpecials, mods, false);
  763. }
  764. private void writeInteger(StringBuffer result, StringBuffer output,
  765. List<Special> numSpecials, Set<StringMod> mods,
  766. boolean showCommas) {
  767. int pos = result.indexOf(".") - 1;
  768. if (pos < 0) {
  769. if (exponent != null && numSpecials == integerSpecials)
  770. pos = result.indexOf("E") - 1;
  771. else
  772. pos = result.length() - 1;
  773. }
  774. int strip;
  775. for (strip = 0; strip < pos; strip++) {
  776. char resultCh = result.charAt(strip);
  777. if (resultCh != '0' && resultCh != ',')
  778. break;
  779. }
  780. ListIterator<Special> it = numSpecials.listIterator(numSpecials.size());
  781. boolean followWithComma = false;
  782. Special lastOutputIntegerDigit = null;
  783. int digit = 0;
  784. while (it.hasPrevious()) {
  785. char resultCh;
  786. if (pos >= 0)
  787. resultCh = result.charAt(pos);
  788. else {
  789. // If result is shorter than field, pretend there are leading zeros
  790. resultCh = '0';
  791. }
  792. Special s = it.previous();
  793. followWithComma = showCommas && digit > 0 && digit % 3 == 0;
  794. boolean zeroStrip = false;
  795. if (resultCh != '0' || s.ch == '0' || s.ch == '?' || pos >= strip) {
  796. zeroStrip = s.ch == '?' && pos < strip;
  797. output.setCharAt(s.pos, (zeroStrip ? ' ' : resultCh));
  798. lastOutputIntegerDigit = s;
  799. }
  800. if (followWithComma) {
  801. mods.add(insertMod(s, zeroStrip ? " " : ",", StringMod.AFTER));
  802. followWithComma = false;
  803. }
  804. digit++;
  805. --pos;
  806. }
  807. StringBuffer extraLeadingDigits = new StringBuffer();
  808. if (pos >= 0) {
  809. // We ran out of places to put digits before we ran out of digits; put this aside so we can add it later
  810. ++pos; // pos was decremented at the end of the loop above when the iterator was at its end
  811. extraLeadingDigits = new StringBuffer(result.substring(0, pos));
  812. if (showCommas) {
  813. while (pos > 0) {
  814. if (digit > 0 && digit % 3 == 0)
  815. extraLeadingDigits.insert(pos, ',');
  816. digit++;
  817. --pos;
  818. }
  819. }
  820. mods.add(insertMod(lastOutputIntegerDigit, extraLeadingDigits,
  821. StringMod.BEFORE));
  822. }
  823. }
  824. private void writeFractional(StringBuffer result, StringBuffer output) {
  825. int digit;
  826. int strip;
  827. ListIterator<Special> it;
  828. if (fractionalSpecials.size() > 0) {
  829. digit = result.indexOf(".") + 1;
  830. if (exponent != null)
  831. strip = result.indexOf("e") - 1;
  832. else
  833. strip = result.length() - 1;
  834. while (strip > digit && result.charAt(strip) == '0')
  835. strip--;
  836. it = fractionalSpecials.listIterator();
  837. while (it.hasNext()) {
  838. Special s = it.next();
  839. char resultCh = result.charAt(digit);
  840. if (resultCh != '0' || s.ch == '0' || digit < strip)
  841. output.setCharAt(s.pos, resultCh);
  842. else if (s.ch == '?') {
  843. // This is when we're in trailing zeros, and the format is '?'. We still strip out remaining '#'s later
  844. output.setCharAt(s.pos, ' ');
  845. }
  846. digit++;
  847. }
  848. }
  849. }
  850. /**
  851. * {@inheritDoc}
  852. * <p/>
  853. * For a number, this is <tt>"#"</tt> for integer values, and <tt>"#.#"</tt>
  854. * for floating-point values.
  855. */
  856. public void simpleValue(StringBuffer toAppendTo, Object value) {
  857. SIMPLE_NUMBER.formatValue(toAppendTo, value);
  858. }
  859. /**
  860. * Based on org.apache.commons.math.fraction.Fraction from Apache Commons-Math.
  861. * YK: The only reason of having this inner class is to avoid dependency on the Commons-Math jar.
  862. */
  863. private static class Fraction {
  864. /** The denominator. */
  865. private final int denominator;
  866. /** The numerator. */
  867. private final int numerator;
  868. /**
  869. * Create a fraction given the double value and either the maximum error
  870. * allowed or the maximum number of denominator digits.
  871. *
  872. * @param value the double value to convert to a fraction.
  873. * @param epsilon maximum error allowed. The resulting fraction is within
  874. * <code>epsilon</code> of <code>value</code>, in absolute terms.
  875. * @param maxDenominator maximum denominator value allowed.
  876. * @param maxIterations maximum number of convergents
  877. * @throws RuntimeException if the continued fraction failed to
  878. * converge.
  879. */
  880. private Fraction(double value, double epsilon, int maxDenominator, int maxIterations)
  881. {
  882. long overflow = Integer.MAX_VALUE;
  883. double r0 = value;
  884. long a0 = (long)Math.floor(r0);
  885. if (a0 > overflow) {
  886. throw new IllegalArgumentException("Overflow trying to convert "+value+" to fraction ("+a0+"/"+1l+")");
  887. }
  888. // check for (almost) integer arguments, which should not go
  889. // to iterations.
  890. if (Math.abs(a0 - value) < epsilon) {
  891. this.numerator = (int) a0;
  892. this.denominator = 1;
  893. return;
  894. }
  895. long p0 = 1;
  896. long q0 = 0;
  897. long p1 = a0;
  898. long q1 = 1;
  899. long p2;
  900. long q2;
  901. int n = 0;
  902. boolean stop = false;
  903. do {
  904. ++n;
  905. double r1 = 1.0 / (r0 - a0);
  906. long a1 = (long)Math.floor(r1);
  907. p2 = (a1 * p1) + p0;
  908. q2 = (a1 * q1) + q0;
  909. if ((p2 > overflow) || (q2 > overflow)) {
  910. throw new RuntimeException("Overflow trying to convert "+value+" to fraction ("+p2+"/"+q2+")");
  911. }
  912. double convergent = (double)p2 / (double)q2;
  913. if (n < maxIterations && Math.abs(convergent - value) > epsilon && q2 < maxDenominator) {
  914. p0 = p1;
  915. p1 = p2;
  916. q0 = q1;
  917. q1 = q2;
  918. a0 = a1;
  919. r0 = r1;
  920. } else {
  921. stop = true;
  922. }
  923. } while (!stop);
  924. if (n >= maxIterations) {
  925. throw new RuntimeException("Unable to convert "+value+" to fraction after "+maxIterations+" iterations");
  926. }
  927. if (q2 < maxDenominator) {
  928. this.numerator = (int) p2;
  929. this.denominator = (int) q2;
  930. } else {
  931. this.numerator = (int) p1;
  932. this.denominator = (int) q1;
  933. }
  934. }
  935. /**
  936. * Create a fraction given the double value and maximum denominator.
  937. * <p>
  938. * References:
  939. * <ul>
  940. * <li><a href="http://mathworld.wolfram.com/ContinuedFraction.html">
  941. * Continued Fraction</a> equations (11) and (22)-(26)</li>
  942. * </ul>
  943. * </p>
  944. * @param value the double value to convert to a fraction.
  945. * @param maxDenominator The maximum allowed value for denominator
  946. * @throws RuntimeException if the continued fraction failed to
  947. * converge
  948. */
  949. public Fraction(double value, int maxDenominator)
  950. {
  951. this(value, 0, maxDenominator, 100);
  952. }
  953. /**
  954. * Access the denominator.
  955. * @return the denominator.
  956. */
  957. public int getDenominator() {
  958. return denominator;
  959. }
  960. /**
  961. * Access the numerator.
  962. * @return the numerator.
  963. */
  964. public int getNumerator() {
  965. return numerator;
  966. }
  967. }
  968. }