All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.poi.ss.usermodel.FractionFormat Maven / Gradle / Ivy

Go to download

The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.

There is a newer version: 62
Show newest version
/*
 * 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.usermodel;

import java.math.BigDecimal;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.poi.ss.format.SimpleFraction;
import org.apache.poi.ss.formula.eval.NotImplementedException;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;

/**
 * 

Format class that handles Excel style fractions, such as "# #/#" and "#/###"

* *

As of this writing, this is still not 100% accurate, but it does a reasonable job * of trying to mimic Excel's fraction calculations. It does not currently * maintain Excel's spacing.

* *

This class relies on a method lifted nearly verbatim from org.apache.math.fraction. * If further uses for Commons Math are found, we will consider adding it as a dependency. * For now, we have in-lined the one method to keep things simple.

*/ @SuppressWarnings("serial") public class FractionFormat extends Format { private static final POILogger LOGGER = POILogFactory.getLogger(FractionFormat.class); private static final Pattern DENOM_FORMAT_PATTERN = Pattern.compile("(?:(#+)|(\\d+))"); //this was chosen to match the earlier limitation of max denom power //it can be expanded to get closer to Excel's calculations //with custom formats # #/######### //but as of this writing, the numerators and denominators //with formats of that nature on very small values were quite //far from Excel's calculations private static final int MAX_DENOM_POW = 4; //there are two options: //a) an exact denominator is specified in the formatString //b) the maximum denominator can be calculated from the formatString private final int exactDenom; private final int maxDenom; private final String wholePartFormatString; /** * Single parameter ctor * @param denomFormatString The format string for the denominator */ public FractionFormat(String wholePartFormatString, String denomFormatString) { this.wholePartFormatString = wholePartFormatString; // initialize exactDenom and maxDenom Matcher m = DENOM_FORMAT_PATTERN.matcher(denomFormatString); int tmpExact = -1; int tmpMax = -1; if (m.find()){ if (m.group(2) != null){ try{ tmpExact = Integer.parseInt(m.group(2)); //if the denom is 0, fall back to the default: tmpExact=100 if (tmpExact == 0){ tmpExact = -1; } } catch (NumberFormatException e){ // should not happen because the pattern already verifies that this is a number, // but a number larger than Integer.MAX_VALUE can cause it, // so throw an exception if we somehow end up here throw new IllegalStateException(e); } } else if (m.group(1) != null) { int len = m.group(1).length(); len = len > MAX_DENOM_POW ? MAX_DENOM_POW : len; tmpMax = (int)Math.pow(10, len); } else { tmpExact = 100; } } if (tmpExact <= 0 && tmpMax <= 0){ //use 100 as the default denom if something went horribly wrong tmpExact = 100; } exactDenom = tmpExact; maxDenom = tmpMax; } @SuppressWarnings("squid:S2111") public String format(Number num) { final BigDecimal doubleValue = new BigDecimal(num.doubleValue()); final boolean isNeg = doubleValue.compareTo(BigDecimal.ZERO) < 0; final BigDecimal absValue = doubleValue.abs(); final BigDecimal wholePart = new BigDecimal(absValue.toBigInteger()); final BigDecimal decPart = absValue.remainder(BigDecimal.ONE); if (wholePart.add(decPart).compareTo(BigDecimal.ZERO) == 0) { return "0"; } // if the absolute value is smaller than 1 over the exact or maxDenom // you can stop here and return "0" // reciprocal is result of an int devision ... and so it's nearly always 0 // double reciprocal = 1/Math.max(exactDenom, maxDenom); // if (absDoubleValue < reciprocal) { // return "0"; // } //this is necessary to prevent overflow in the maxDenom calculation if (decPart.compareTo(BigDecimal.ZERO) == 0){ StringBuilder sb = new StringBuilder(); if (isNeg){ sb.append("-"); } sb.append(wholePart); return sb.toString(); } final SimpleFraction fract; try { //this should be the case because of the constructor if (exactDenom > 0){ fract = SimpleFraction.buildFractionExactDenominator(decPart.doubleValue(), exactDenom); } else { fract = SimpleFraction.buildFractionMaxDenominator(decPart.doubleValue(), maxDenom); } } catch (RuntimeException e){ LOGGER.log(POILogger.WARN, "Can't format fraction", e); return Double.toString(doubleValue.doubleValue()); } StringBuilder sb = new StringBuilder(); //now format the results if (isNeg){ sb.append("-"); } //if whole part has to go into the numerator if (wholePartFormatString == null || wholePartFormatString.isEmpty()){ final int fden = fract.getDenominator(); final int fnum = fract.getNumerator(); BigDecimal trueNum = wholePart.multiply(new BigDecimal(fden)).add(new BigDecimal(fnum)); sb.append(trueNum.toBigInteger()).append("/").append(fden); return sb.toString(); } //short circuit if fraction is 0 or 1 if (fract.getNumerator() == 0){ sb.append(wholePart); return sb.toString(); } else if (fract.getNumerator() == fract.getDenominator()){ sb.append(wholePart.add(BigDecimal.ONE)); return sb.toString(); } //as mentioned above, this ignores the exact space formatting in Excel if (wholePart.compareTo(BigDecimal.ZERO) > 0){ sb.append(wholePart).append(" "); } sb.append(fract.getNumerator()).append("/").append(fract.getDenominator()); return sb.toString(); } public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { return toAppendTo.append(format((Number)obj)); } public Object parseObject(String source, ParsePosition pos) { throw new NotImplementedException("Reverse parsing not supported"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy