/* ==================================================================== | |
Licensed to the Apache Software Foundation (ASF) under one or more | |
contributor license agreements. See the NOTICE file distributed with | |
this work for additional information regarding copyright ownership. | |
The ASF licenses this file to You under the Apache License, Version 2.0 | |
(the "License"); you may not use this file except in compliance with | |
the License. You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, software | |
distributed under the License is distributed on an "AS IS" BASIS, | |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License. | |
==================================================================== */ | |
package org.apache.poi.ss.format; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.ListIterator; | |
import java.util.regex.Matcher; | |
import org.apache.poi.ss.format.CellFormatPart.PartHandler; | |
import org.apache.poi.ss.format.CellNumberFormatter.Special; | |
import org.apache.poi.util.Internal; | |
/** | |
* Internal helper class for CellNumberFormatter | |
*/ | |
@Internal | |
public class CellNumberPartHandler implements PartHandler { | |
private char insertSignForExponent; | |
private double scale = 1; | |
private Special decimalPoint; | |
private Special slash; | |
private Special exponent; | |
private Special numerator; | |
private final List<Special> specials = new LinkedList<Special>(); | |
private boolean improperFraction; | |
public String handlePart(Matcher m, String part, CellFormatType type, StringBuffer descBuf) { | |
int pos = descBuf.length(); | |
char firstCh = part.charAt(0); | |
switch (firstCh) { | |
case 'e': | |
case 'E': | |
// See comment in writeScientific -- exponent handling is complex. | |
// (1) When parsing the format, remove the sign from after the 'e' and | |
// put it before the first digit of the exponent. | |
if (exponent == null && specials.size() > 0) { | |
exponent = new Special('.', pos); | |
specials.add(exponent); | |
insertSignForExponent = part.charAt(1); | |
return part.substring(0, 1); | |
} | |
break; | |
case '0': | |
case '?': | |
case '#': | |
if (insertSignForExponent != '\0') { | |
specials.add(new Special(insertSignForExponent, pos)); | |
descBuf.append(insertSignForExponent); | |
insertSignForExponent = '\0'; | |
pos++; | |
} | |
for (int i = 0; i < part.length(); i++) { | |
char ch = part.charAt(i); | |
specials.add(new Special(ch, pos + i)); | |
} | |
break; | |
case '.': | |
if (decimalPoint == null && specials.size() > 0) { | |
decimalPoint = new Special('.', pos); | |
specials.add(decimalPoint); | |
} | |
break; | |
case '/': | |
//!! This assumes there is a numerator and a denominator, but these are actually optional | |
if (slash == null && specials.size() > 0) { | |
numerator = previousNumber(); | |
// If the first number in the whole format is the numerator, the | |
// entire number should be printed as an improper fraction | |
improperFraction |= (numerator == firstDigit(specials)); | |
slash = new Special('.', pos); | |
specials.add(slash); | |
} | |
break; | |
case '%': | |
// don't need to remember because we don't need to do anything with these | |
scale *= 100; | |
break; | |
default: | |
return null; | |
} | |
return part; | |
} | |
public double getScale() { | |
return scale; | |
} | |
public Special getDecimalPoint() { | |
return decimalPoint; | |
} | |
public Special getSlash() { | |
return slash; | |
} | |
public Special getExponent() { | |
return exponent; | |
} | |
public Special getNumerator() { | |
return numerator; | |
} | |
public List<Special> getSpecials() { | |
return specials; | |
} | |
public boolean isImproperFraction() { | |
return improperFraction; | |
} | |
private Special previousNumber() { | |
ListIterator<Special> it = specials.listIterator(specials.size()); | |
while (it.hasPrevious()) { | |
Special s = it.previous(); | |
if (isDigitFmt(s)) { | |
Special last = s; | |
while (it.hasPrevious()) { | |
s = it.previous(); | |
// it has to be continuous digits | |
if (last.pos - s.pos > 1 || !isDigitFmt(s)) { | |
break; | |
} | |
last = s; | |
} | |
return last; | |
} | |
} | |
return null; | |
} | |
private static boolean isDigitFmt(Special s) { | |
return s.ch == '0' || s.ch == '?' || s.ch == '#'; | |
} | |
private static Special firstDigit(List<Special> specials) { | |
for (Special s : specials) { | |
if (isDigitFmt(s)) { | |
return s; | |
} | |
} | |
return null; | |
} | |
} |