aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/org/apache')
-rw-r--r--src/java/org/apache/poi/hssf/record/FontRecord.java57
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java30
-rw-r--r--src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java9
-rw-r--r--src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java186
-rw-r--r--src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java8
-rw-r--r--src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java12
-rw-r--r--src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java32
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawFactory.java32
-rw-r--r--src/java/org/apache/poi/sl/draw/DrawShape.java94
-rw-r--r--src/java/org/apache/poi/ss/formula/functions/Delta.java45
-rw-r--r--src/java/org/apache/poi/ss/formula/functions/Fixed.java11
-rw-r--r--src/java/org/apache/poi/ss/formula/functions/Irr.java26
-rw-r--r--src/java/org/apache/poi/ss/usermodel/DataFormatter.java128
-rw-r--r--src/java/org/apache/poi/ss/usermodel/ExcelGeneralNumberFormat.java3
-rw-r--r--src/java/org/apache/poi/ss/usermodel/FractionFormat.java29
15 files changed, 348 insertions, 354 deletions
diff --git a/src/java/org/apache/poi/hssf/record/FontRecord.java b/src/java/org/apache/poi/hssf/record/FontRecord.java
index 5296d5e3ce..cf673397d6 100644
--- a/src/java/org/apache/poi/hssf/record/FontRecord.java
+++ b/src/java/org/apache/poi/hssf/record/FontRecord.java
@@ -17,6 +17,8 @@
package org.apache.poi.hssf.record;
+import java.util.Objects;
+
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.HexDump;
@@ -30,7 +32,8 @@ import org.apache.poi.util.StringUtil;
* REFERENCE: PG 315 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)
*/
public final class FontRecord extends StandardRecord {
- public final static short sid = 0x0031; // docs are wrong (0x231 Microsoft Support site article Q184647)
+ // docs are wrong (0x231 Microsoft Support site article Q184647)
+ public final static short sid = 0x0031;
public final static short SS_NONE = 0;
public final static short SS_SUPER = 1;
public final static short SS_SUB = 2;
@@ -39,7 +42,8 @@ public final class FontRecord extends StandardRecord {
public final static byte U_DOUBLE = 2;
public final static byte U_SINGLE_ACCOUNTING = 0x21;
public final static byte U_DOUBLE_ACCOUNTING = 0x22;
- private short field_1_font_height; // in units of .05 of a point
+ // in units of .05 of a point
+ private short field_1_font_height;
private short field_2_attributes;
// 0 0x01 - Reserved bit must be 0
@@ -359,24 +363,22 @@ public final class FontRecord extends StandardRecord {
}
public String toString() {
- StringBuilder sb = new StringBuilder();
-
- sb.append("[FONT]\n");
- sb.append(" .fontheight = ").append(HexDump.shortToHex(getFontHeight())).append("\n");
- sb.append(" .attributes = ").append(HexDump.shortToHex(getAttributes())).append("\n");
- sb.append(" .italic = ").append(isItalic()).append("\n");
- sb.append(" .strikout = ").append(isStruckout()).append("\n");
- sb.append(" .macoutlined= ").append(isMacoutlined()).append("\n");
- sb.append(" .macshadowed= ").append(isMacshadowed()).append("\n");
- sb.append(" .colorpalette = ").append(HexDump.shortToHex(getColorPaletteIndex())).append("\n");
- sb.append(" .boldweight = ").append(HexDump.shortToHex(getBoldWeight())).append("\n");
- sb.append(" .supersubscript= ").append(HexDump.shortToHex(getSuperSubScript())).append("\n");
- sb.append(" .underline = ").append(HexDump.byteToHex(getUnderline())).append("\n");
- sb.append(" .family = ").append(HexDump.byteToHex(getFamily())).append("\n");
- sb.append(" .charset = ").append(HexDump.byteToHex(getCharset())).append("\n");
- sb.append(" .fontname = ").append(getFontName()).append("\n");
- sb.append("[/FONT]\n");
- return sb.toString();
+ return
+ "[FONT]\n" +
+ " .fontheight = " + HexDump.shortToHex(getFontHeight()) + "\n" +
+ " .attributes = " + HexDump.shortToHex(getAttributes()) + "\n" +
+ " .italic = " + isItalic() + "\n" +
+ " .strikout = " + isStruckout() + "\n" +
+ " .macoutlined= " + isMacoutlined() + "\n" +
+ " .macshadowed= " + isMacshadowed() + "\n" +
+ " .colorpalette = " + HexDump.shortToHex(getColorPaletteIndex()) + "\n" +
+ " .boldweight = " + HexDump.shortToHex(getBoldWeight()) + "\n" +
+ " .supersubscript= " + HexDump.shortToHex(getSuperSubScript()) + "\n" +
+ " .underline = " + HexDump.byteToHex(getUnderline()) + "\n" +
+ " .family = " + HexDump.byteToHex(getFamily()) + "\n" +
+ " .charset = " + HexDump.byteToHex(getCharset()) + "\n" +
+ " .fontname = " + getFontName() + "\n" +
+ "[/FONT]\n";
}
public void serialize(LittleEndianOutput out) {
@@ -421,7 +423,7 @@ public final class FontRecord extends StandardRecord {
* Clones all the font style information from another
* FontRecord, onto this one. This
* will then hold all the same font style options.
- *
+ *
* @param source the record to clone the properties from
*/
public void cloneStyleFrom(FontRecord source) {
@@ -464,12 +466,13 @@ public final class FontRecord extends StandardRecord {
* for exact contents, because normally the
* font record's position makes a big
* difference too.
- *
+ *
* @param other the record to compare with
- *
+ *
* @return true, if the properties match
*/
public boolean sameProperties(FontRecord other) {
+
return
field_1_font_height == other.field_1_font_height &&
field_2_attributes == other.field_2_attributes &&
@@ -480,15 +483,11 @@ public final class FontRecord extends StandardRecord {
field_7_family == other.field_7_family &&
field_8_charset == other.field_8_charset &&
field_9_zero == other.field_9_zero &&
- stringEquals(this.field_11_font_name, other.field_11_font_name)
+ Objects.equals(this.field_11_font_name, other.field_11_font_name)
;
}
public boolean equals(Object o) {
- return (o instanceof FontRecord) ? sameProperties((FontRecord)o) : false;
- }
-
- private static boolean stringEquals(String s1, String s2) {
- return (s1 == s2 || (s1 != null && s1.equals(s2)));
+ return (o instanceof FontRecord) && sameProperties((FontRecord) o);
}
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java b/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java
index a3879a9194..68cbc96c9e 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFCellStyle.java
@@ -108,7 +108,7 @@ public final class HSSFCellStyle implements CellStyle {
}
// we keep the cached data in ThreadLocal members in order to
- // avoid multi-threading issues when different workbooks are accessed in
+ // avoid multi-threading issues when different workbooks are accessed in
// multiple threads at the same time
private static final ThreadLocal<Short> lastDateFormat = new ThreadLocal<Short>() {
@Override
@@ -264,7 +264,7 @@ public final class HSSFCellStyle implements CellStyle {
public void setQuotePrefixed(boolean quotePrefix) {
_format.set123Prefix(quotePrefix);
}
-
+
/**
* Is "Quote Prefix" or "123 Prefix" enabled for the cell?
*/
@@ -272,7 +272,7 @@ public final class HSSFCellStyle implements CellStyle {
public boolean getQuotePrefixed() {
return _format.get123Prefix();
}
-
+
/**
* set the type of horizontal alignment for the cell
* @param align - the type of alignment
@@ -340,8 +340,8 @@ public final class HSSFCellStyle implements CellStyle {
/**
* set the degree of rotation for the text in the cell
*
- * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF
- * uses values from 0 to 180 degrees. The implementations of this method will map between these two value-ranges
+ * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF
+ * uses values from 0 to 180 degrees. The implementations of this method will map between these two value-ranges
* accordingly, however the corresponding getter is returning values in the range mandated by the current type
* of Excel file-format that this CellStyle is applied to.
*
@@ -352,7 +352,7 @@ public final class HSSFCellStyle implements CellStyle {
{
if (rotation == 0xff) {
// Special cases for vertically aligned text
- }
+ }
else if ((rotation < 0)&&(rotation >= -90)) {
//Take care of the funny 4th quadrant issue
//The 4th quadrant (-1 to -90) is stored as (91 to 180)
@@ -407,7 +407,7 @@ public final class HSSFCellStyle implements CellStyle {
{
return _format.getIndent();
}
-
+
/**
* set the type of border to use for the left border of the cell
* @param border type
@@ -575,7 +575,7 @@ public final class HSSFCellStyle implements CellStyle {
{
return _format.getBottomBorderPaletteIdx();
}
-
+
/**
* setting to one fills the cell with the foreground color... No idea about
* other values
@@ -674,7 +674,7 @@ public final class HSSFCellStyle implements CellStyle {
}
return result;
}
-
+
@Override
public HSSFColor getFillBackgroundColorColor() {
HSSFPalette pallette = new HSSFPalette(
@@ -769,7 +769,7 @@ public final class HSSFCellStyle implements CellStyle {
public boolean getShrinkToFit() {
return _format.getShrinkToFit();
}
-
+
/**
* Get the reading order, for RTL/LTR ordering of
* the text.
@@ -792,7 +792,7 @@ public final class HSSFCellStyle implements CellStyle {
public void setReadingOrder(short order) {
_format.setReadingOrder(order);
}
-
+
/**
* Verifies that this style belongs to the supplied Workbook.
* Will throw an exception if it belongs to a different one.
@@ -836,9 +836,9 @@ public final class HSSFCellStyle implements CellStyle {
if(_workbook != source._workbook) {
lastDateFormat.set(Short.MIN_VALUE);
- lastFormats.set(null);
- getDataFormatStringCache.set(null);
-
+ lastFormats.remove();
+ getDataFormatStringCache.remove();
+
// Then we need to clone the format string,
// and update the format record for this
short fmt = (short)_workbook.createFormat(source.getDataFormatString() );
@@ -894,5 +894,5 @@ public final class HSSFCellStyle implements CellStyle {
}
return false;
}
-
+
}
diff --git a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
index 5ad77ddd62..827ee4c0a7 100644
--- a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
+++ b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
@@ -21,10 +21,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
-import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.ShortBufferException;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.util.IOUtils;
@@ -82,7 +79,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
@Override
public int read() throws IOException {
byte[] b = { 0 };
- return (read(b) == 1) ? b[0] : -1;
+ return (read(b) == 1) ? (b[0] & 0xFF) : -1;
}
// do not implement! -> recursion
@@ -222,7 +219,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
/**
* Used when BIFF header fields (sid, size) are being read. The internal
* {@link Cipher} instance must step even when unencrypted bytes are read
- *
+ *
*/
@Override
public void readPlain(byte[] b, int off, int len) {
@@ -236,7 +233,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
readBytes = read(b, off, len, true);
total += Math.max(0, readBytes);
} while (readBytes > -1 && total < len);
-
+
if (total < len) {
throw new EOFException("buffer underrun");
}
diff --git a/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java b/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
index 28e2bd7537..46cd58128a 100644
--- a/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
+++ b/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
@@ -16,7 +16,7 @@
==================================================================== */
package org.apache.poi.poifs.crypt;
-import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.security.DigestException;
import java.security.GeneralSecurityException;
import java.security.Key;
@@ -44,59 +44,62 @@ import org.apache.poi.util.StringUtil;
* Helper functions used for standard and agile encryption
*/
@Internal
-public class CryptoFunctions {
+public final class CryptoFunctions {
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 100_000;
+ private CryptoFunctions() {
+ }
+
/**
* <p><cite>2.3.4.7 ECMA-376 Document Encryption Key Generation (Standard Encryption)<br>
* 2.3.4.11 Encryption Key Generation (Agile Encryption)</cite></p>
- *
+ *
* <p>The encryption key for ECMA-376 document encryption [ECMA-376] using agile
* encryption MUST be generated by using the following method, which is derived from PKCS #5:
* <a href="https://www.ietf.org/rfc/rfc2898.txt">Password-Based Cryptography Version 2.0 [RFC2898]</a>.</p>
- *
+ *
* <p>Let H() be a hashing algorithm as determined by the PasswordKeyEncryptor.hashAlgorithm
* element, H_n be the hash data of the n-th iteration, and a plus sign (+) represent concatenation.
* The password MUST be provided as an array of Unicode characters. Limitations on the length of the
* password and the characters used by the password are implementation-dependent.
* The initial password hash is generated as follows:</p>
- *
- *
+ *
+ *
* <pre>H_0 = H(salt + password)</pre>
- *
+ *
* <p>The salt used MUST be generated randomly. The salt MUST be stored in the
* PasswordKeyEncryptor.saltValue element contained within the \EncryptionInfo stream as
* specified in section 2.3.4.10. The hash is then iterated by using the following approach:</p>
- *
+ *
* <pre>H_n = H(iterator + H_n-1)</pre>
- *
+ *
* <p>where iterator is an unsigned 32-bit value that is initially set to 0x00000000 and then incremented
* monotonically on each iteration until PasswordKey.spinCount iterations have been performed.
* The value of iterator on the last iteration MUST be one less than PasswordKey.spinCount.</p>
- *
+ *
* <p>For POI, H_final will be calculated by {@link #generateKey(byte[],HashAlgorithm,byte[],int)}</p>
*
- * @param password
- * @param hashAlgorithm
- * @param salt
- * @param spinCount
+ * @param password the password
+ * @param hashAlgorithm the hash algorithm
+ * @param salt the initial salt value
+ * @param spinCount the repetition count
* @return the hashed password
*/
public static byte[] hashPassword(String password, HashAlgorithm hashAlgorithm, byte[] salt, int spinCount) {
return hashPassword(password, hashAlgorithm, salt, spinCount, true);
}
-
+
/**
* Generalized method for read and write protection hash generation.
* The difference is, read protection uses the order iterator then hash in the hash loop, whereas write protection
* uses first the last hash value and then the current iterator value
*
- * @param password
- * @param hashAlgorithm
- * @param salt
- * @param spinCount
+ * @param password the pasword
+ * @param hashAlgorithm the hash algorighm
+ * @param salt the initial salt value
+ * @param spinCount the repetition count
* @param iteratorFirst if true, the iterator is hashed before the n-1 hash value,
* if false the n-1 hash value is applied first
* @return the hashed password
@@ -107,16 +110,16 @@ public class CryptoFunctions {
if (password == null) {
password = Decryptor.DEFAULT_PASSWORD;
}
-
+
MessageDigest hashAlg = getMessageDigest(hashAlgorithm);
-
+
hashAlg.update(salt);
byte[] hash = hashAlg.digest(StringUtil.getToUnicodeLE(password));
byte[] iterator = new byte[LittleEndianConsts.INT_SIZE];
byte[] first = (iteratorFirst ? iterator : hash);
byte[] second = (iteratorFirst ? hash : iterator);
-
+
try {
for (int i = 0; i < spinCount; i++) {
LittleEndian.putInt(iterator, 0, i);
@@ -128,13 +131,13 @@ public class CryptoFunctions {
} catch (DigestException e) {
throw new EncryptedDocumentException("error in password hashing");
}
-
+
return hash;
- }
+ }
/**
* <p><cite>2.3.4.12 Initialization Vector Generation (Agile Encryption)</cite></p>
- *
+ *
* <p>Initialization vectors are used in all cases for agile encryption. An initialization vector MUST be
* generated by using the following method, where H() is a hash function that MUST be the same as
* specified in section 2.3.4.11 and a plus sign (+) represents concatenation:</p>
@@ -147,7 +150,7 @@ public class CryptoFunctions {
* corresponding to the cipherAlgorithm attribute, pad the array of bytes by appending 0x36 until
* the array is blockSize bytes. If the array of bytes is larger than blockSize bytes, truncate the
* array to blockSize bytes.</li>
- * </ul>
+ * </ul>
**/
public static byte[] generateIv(HashAlgorithm hashAlgorithm, byte[] salt, byte[] blockKey, int blockSize) {
byte[] iv = salt;
@@ -161,23 +164,23 @@ public class CryptoFunctions {
/**
* <p><cite>2.3.4.11 Encryption Key Generation (Agile Encryption)</cite></p>
- *
+ *
* <p>The final hash data that is used for an encryption key is then generated by using the following
* method:</p>
- *
+ *
* <pre>H_final = H(H_n + blockKey)</pre>
- *
+ *
* <p>where blockKey represents an array of bytes used to prevent two different blocks from encrypting
* to the same cipher text.</p>
- *
+ *
* <p>If the size of the resulting H_final is smaller than that of PasswordKeyEncryptor.keyBits, the key
* MUST be padded by appending bytes with a value of 0x36. If the hash value is larger in size than
- * PasswordKeyEncryptor.keyBits, the key is obtained by truncating the hash value.</p>
+ * PasswordKeyEncryptor.keyBits, the key is obtained by truncating the hash value.</p>
*
- * @param passwordHash
- * @param hashAlgorithm
- * @param blockKey
- * @param keySize
+ * @param passwordHash the hashed password byte
+ * @param hashAlgorithm the hash algorithm
+ * @param blockKey the block key
+ * @param keySize the key size
* @return intermediate key
*/
public static byte[] generateKey(byte[] passwordHash, HashAlgorithm hashAlgorithm, byte[] blockKey, int keySize) {
@@ -198,9 +201,8 @@ public class CryptoFunctions {
* @param vec the initialization vector (IV), can be null
* @param cipherMode Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
* @return the requested cipher
- * @throws GeneralSecurityException
* @throws EncryptedDocumentException if the initialization failed or if an algorithm was specified,
- * which depends on a missing bouncy castle provider
+ * which depends on a missing bouncy castle provider
*/
public static Cipher getCipher(SecretKey key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode) {
return getCipher(key, cipherAlgorithm, chain, vec, cipherMode, null);
@@ -218,14 +220,13 @@ public class CryptoFunctions {
* @param cipherMode Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
* @param padding the padding (null = NOPADDING, ANSIX923Padding, PKCS5Padding, PKCS7Padding, ISO10126Padding, ...)
* @return the requested cipher
- * @throws GeneralSecurityException
* @throws EncryptedDocumentException if the initialization failed or if an algorithm was specified,
- * which depends on a missing bouncy castle provider
+ * which depends on a missing bouncy castle provider
*/
public static Cipher getCipher(Key key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode, String padding) {
int keySizeInBytes = key.getEncoded().length;
if (padding == null) padding = "NoPadding";
-
+
try {
// Ensure the JCE policies files allow for this sized key
if (Cipher.getMaxAllowedKeyLength(cipherAlgorithm.jceId) < keySizeInBytes*8) {
@@ -241,7 +242,7 @@ public class CryptoFunctions {
} else {
cipher = Cipher.getInstance(cipherAlgorithm.jceId + "/" + chain.jceId + "/" + padding);
}
-
+
if (vec == null) {
cipher.init(cipherMode, key);
} else {
@@ -257,10 +258,10 @@ public class CryptoFunctions {
} catch (GeneralSecurityException e) {
throw new EncryptedDocumentException(e);
}
- }
-
+ }
+
/**
- * Returns a new byte array with a truncated to the given size.
+ * Returns a new byte array with a truncated to the given size.
* If the hash has less then size bytes, it will be filled with 0x36-bytes
*
* @param hash the to be truncated/filled hash byte array
@@ -272,7 +273,7 @@ public class CryptoFunctions {
}
/**
- * Returns a new byte array with a truncated to the given size.
+ * Returns a new byte array with a truncated to the given size.
* If the hash has less then size bytes, it will be filled with 0-bytes
*
* @param hash the to be truncated/filled hash byte array
@@ -282,16 +283,16 @@ public class CryptoFunctions {
public static byte[] getBlock0(byte[] hash, int size) {
return getBlockX(hash, size, (byte)0);
}
-
+
private static byte[] getBlockX(byte[] hash, int size, byte fill) {
if (hash.length == size) return hash;
-
+
byte[] result = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
Arrays.fill(result, fill);
System.arraycopy(hash, 0, result, 0, Math.min(result.length, hash.length));
return result;
}
-
+
public static MessageDigest getMessageDigest(HashAlgorithm hashAlgorithm) {
try {
if (hashAlgorithm.needsBouncyCastle) {
@@ -304,7 +305,7 @@ public class CryptoFunctions {
throw new EncryptedDocumentException("hash algo not supported", e);
}
}
-
+
public static Mac getMac(HashAlgorithm hashAlgorithm) {
try {
if (hashAlgorithm.needsBouncyCastle) {
@@ -323,7 +324,7 @@ public class CryptoFunctions {
if (Security.getProvider("BC") != null) {
return;
}
-
+
try {
ClassLoader cl = CryptoFunctions.class.getClassLoader();
String bcProviderName = "org.bouncycastle.jce.provider.BouncyCastleProvider";
@@ -370,7 +371,7 @@ public class CryptoFunctions {
* @see <a href="http://msdn.microsoft.com/en-us/library/dd926947.aspx">2.3.7.1 Binary Document Password Verifier Derivation Method 1</a>
* @see <a href="http://msdn.microsoft.com/en-us/library/dd905229.aspx">2.3.7.4 Binary Document Password Verifier Derivation Method 2</a>
* @see <a href="http://www.ecma-international.org/news/TC45_current_work/Office Open XML Part 4 - Markup Language Reference.pdf">Part 4 - Markup Language Reference - Ecma International - 3.2.12 fileSharing</a>
- *
+ *
* @param password the password
* @return the verifier (actually a short value)
*/
@@ -380,7 +381,7 @@ public class CryptoFunctions {
}
byte[] arrByteChars = toAnsiPassword(password);
-
+
// SET Verifier TO 0x0000
short verifier = 0;
@@ -391,27 +392,27 @@ public class CryptoFunctions {
verifier = rotateLeftBase15Bit(verifier);
verifier ^= arrByteChars[i];
}
-
+
// as we haven't prepended the password length into the input array
// we need to do it now separately ...
verifier = rotateLeftBase15Bit(verifier);
verifier ^= arrByteChars.length;
-
+
// RETURN Verifier BITWISE XOR 0xCE4B
verifier ^= 0xCE4B; // (0x8000 | ('N' << 8) | 'K')
}
-
+
return verifier & 0xFFFF;
}
-
+
/**
* This method generates the xor verifier for word documents &lt; 2007 (method 2).
* Its output will be used as password input for the newer word generations which
* utilize a real hashing algorithm like sha1.
- *
+ *
* @param password the password
* @return the hashed password
- *
+ *
* @see <a href="http://msdn.microsoft.com/en-us/library/dd905229.aspx">2.3.7.4 Binary Document Password Verifier Derivation Method 2</a>
* @see <a href="http://blogs.msdn.com/b/vsod/archive/2010/04/05/how-to-set-the-editing-restrictions-in-word-using-open-xml-sdk-2-0.aspx">How to set the editing restrictions in Word using Open XML SDK 2.0</a>
* @see <a href="http://www.aspose.com/blogs/aspose-blogs/vladimir-averkin/archive/2007/08/20/funny-how-the-new-powerful-cryptography-implemented-in-word-2007-turns-it-into-a-perfect-tool-for-document-password-removal.html">Funny: How the new powerful cryptography implemented in Word 2007 turns it into a perfect tool for document password removal.</a>
@@ -425,42 +426,43 @@ public class CryptoFunctions {
byte[] generatedKey = new byte[4];
//Maximum length of the password is 15 chars.
- final int maxPasswordLength = 15;
-
+ final int maxPasswordLength = 15;
+
if (!password.isEmpty()) {
// Truncate the password to 15 characters
password = password.substring(0, Math.min(password.length(), maxPasswordLength));
byte[] arrByteChars = toAnsiPassword(password);
-
+
// Compute the high-order word of the new key:
- // --> Initialize from the initial code array (see below), depending on the passwords length.
+ // --> Initialize from the initial code array (see below), depending on the passwords length.
int highOrderWord = INITIAL_CODE_ARRAY[arrByteChars.length - 1];
// --> For each character in the password:
- // --> For every bit in the character, starting with the least significant and progressing to (but excluding)
- // the most significant, if the bit is set, XOR the keys high-order word with the corresponding word from
+ // --> For every bit in the character, starting with the least significant and progressing to (but excluding)
+ // the most significant, if the bit is set, XOR the keys high-order word with the corresponding word from
// the Encryption Matrix
- for (int i = 0; i < arrByteChars.length; i++) {
- int tmp = maxPasswordLength - arrByteChars.length + i;
- for (int intBit = 0; intBit < 7; intBit++) {
- if ((arrByteChars[i] & (0x0001 << intBit)) != 0) {
- highOrderWord ^= ENCRYPTION_MATRIX[tmp][intBit];
+ int line = maxPasswordLength - arrByteChars.length;
+ for (byte ch : arrByteChars) {
+ for (int xor : ENCRYPTION_MATRIX[line++]) {
+ if ((ch & 1) == 1) {
+ highOrderWord ^= xor;
}
+ ch >>>= 1;
}
}
-
+
// Compute the low-order word of the new key:
int verifier = createXorVerifier1(password);
// The byte order of the result shall be reversed [password "Example": 0x64CEED7E becomes 7EEDCE64],
// and that value shall be hashed as defined by the attribute values.
-
+
LittleEndian.putShort(generatedKey, 0, (short)verifier);
LittleEndian.putShort(generatedKey, 2, (short)highOrderWord);
}
-
+
return LittleEndian.getInt(generatedKey);
}
@@ -471,16 +473,16 @@ public class CryptoFunctions {
int hashedPassword = createXorVerifier2(password);
return String.format(Locale.ROOT, "%1$08X", hashedPassword);
}
-
+
/**
- * Convenience function which returns the reversed xored-hashed password for further
+ * Convenience function which returns the reversed xored-hashed password for further
* processing in word documents 2007 and newer, which utilize a real hashing algorithm like sha1.
*/
public static String xorHashPasswordReversed(String password) {
int hashedPassword = createXorVerifier2(password);
-
+
return String.format(Locale.ROOT, "%1$02X%2$02X%3$02X%4$02X"
- , ( hashedPassword >>> 0 ) & 0xFF
+ , (hashedPassword) & 0xFF
, ( hashedPassword >>> 8 ) & 0xFF
, ( hashedPassword >>> 16 ) & 0xFF
, ( hashedPassword >>> 24 ) & 0xFF
@@ -492,7 +494,7 @@ public class CryptoFunctions {
*
* @see <a href="http://msdn.microsoft.com/en-us/library/dd924704.aspx">2.3.7.2 Binary Document XOR Array Initialization Method 1</a>
* @see <a href="http://msdn.microsoft.com/en-us/library/dd905229.aspx">2.3.7.4 Binary Document Password Verifier Derivation Method 2</a>
- *
+ *
* @param password the password
* @return the xor key
*/
@@ -503,7 +505,7 @@ public class CryptoFunctions {
}
/**
- * Creates an byte array for xor obfuscation (method 1)
+ * Creates an byte array for xor obfuscation (method 1)
*
* @see <a href="http://msdn.microsoft.com/en-us/library/dd924704.aspx">2.3.7.2 Binary Document XOR Array Initialization Method 1</a>
* @see <a href="http://docs.libreoffice.org/oox/html/binarycodec_8cxx_source.html">Libre Office implementation</a>
@@ -515,17 +517,17 @@ public class CryptoFunctions {
if (password.length() > 15) {
password = password.substring(0, 15);
}
- byte[] passBytes = password.getBytes(Charset.forName("ASCII"));
-
+ byte[] passBytes = password.getBytes(StandardCharsets.US_ASCII);
+
// this code is based on the libre office implementation.
- // The MS-OFFCRYPTO misses some infos about the various rotation sizes
+ // The MS-OFFCRYPTO misses some infos about the various rotation sizes
byte[] obfuscationArray = new byte[16];
System.arraycopy(passBytes, 0, obfuscationArray, 0, passBytes.length);
System.arraycopy(PAD_ARRAY, 0, obfuscationArray, passBytes.length, PAD_ARRAY.length-passBytes.length+1);
-
+
int xorKey = createXorKey1(password);
-
- // rotation of key values is application dependent - Excel = 2 / Word = 7
+
+ // rotation of key values is application dependent - Excel = 2 / Word = 7
int nRotateSize = 2;
byte[] baseKeyLE = {(byte) (xorKey & 0xFF), (byte) ((xorKey >>> 8) & 0xFF)};
@@ -533,26 +535,26 @@ public class CryptoFunctions {
obfuscationArray[i] ^= baseKeyLE[i&1];
obfuscationArray[i] = rotateLeft(obfuscationArray[i], nRotateSize);
}
-
+
return obfuscationArray;
}
-
+
/**
* The provided Unicode password string is converted to a ANSI string
*
* @param password the password
* @return the ansi bytes
- *
+ *
* @see <a href="http://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf">Part 4 - Markup Language Reference - Ecma International - section 3.2.29 (workbookProtection)</a>
*/
private static byte[] toAnsiPassword(String password) {
- // TODO: charset conversion (see ecma spec)
-
+ // TODO: charset conversion (see ecma spec)
+
// Get the single-byte values by iterating through the Unicode characters.
// For each character, if the low byte is not equal to 0, take it.
// Otherwise, take the high byte.
byte[] arrByteChars = new byte[password.length()];
-
+
for (int i = 0; i < password.length(); i++) {
int intTemp = password.charAt(i);
byte lowByte = (byte)(intTemp & 0xFF);
@@ -562,11 +564,11 @@ public class CryptoFunctions {
return arrByteChars;
}
-
+
private static byte rotateLeft(byte bits, int shift) {
return (byte)(((bits & 0xff) << shift) | ((bits & 0xff) >>> (8 - shift)));
}
-
+
private static short rotateLeftBase15Bit(short verifier) {
/*
* IF (Verifier BITWISE AND 0x4000) is 0x0000
diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java
index 361578e967..c2391b1680 100644
--- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java
+++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java
@@ -27,19 +27,19 @@ import org.apache.poi.util.Internal;
/**
* A seekable InputStream, which is used to decrypt/extract the document entries
- * within the encrypted stream
+ * within the encrypted stream
*/
@Internal
/* package */ class CryptoAPIDocumentInputStream extends ByteArrayInputStream {
private Cipher cipher;
private final CryptoAPIDecryptor decryptor;
private byte[] oneByte = {0};
-
+
public void seek(int newpos) {
if (newpos > count) {
throw new ArrayIndexOutOfBoundsException(newpos);
}
-
+
this.pos = newpos;
mark = newpos;
}
@@ -60,7 +60,7 @@ import org.apache.poi.util.Internal;
} catch (ShortBufferException e) {
throw new EncryptedDocumentException(e);
}
- return oneByte[0];
+ return oneByte[0] & 0xFF;
}
@Override
diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java
index fb6fb93e18..3a9cae9b04 100644
--- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java
+++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java
@@ -37,21 +37,21 @@ import org.apache.poi.util.Internal;
this.encryptor = encryptor;
cipher = encryptor.initCipherForBlock(null, 0);
}
-
+
public byte[] getBuf() {
return buf;
}
-
+
public void setSize(int count) {
this.count = count;
}
-
+
public void setBlock(int block) throws GeneralSecurityException {
encryptor.initCipherForBlock(cipher, block);
}
-
+
@Override
- public void write(int b) {
+ public synchronized void write(int b) {
try {
oneByte[0] = (byte)b;
cipher.update(oneByte, 0, 1, oneByte, 0);
@@ -62,7 +62,7 @@ import org.apache.poi.util.Internal;
}
@Override
- public void write(byte[] b, int off, int len) {
+ public synchronized void write(byte[] b, int off, int len) {
try {
cipher.update(b, off, len, b, off);
super.write(b, off, len);
diff --git a/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java b/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java
index e0ad436e2a..26769a94ea 100644
--- a/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java
+++ b/src/java/org/apache/poi/poifs/filesystem/DocumentInputStream.java
@@ -68,9 +68,9 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
/**
* Create an InputStream from the specified DocumentEntry
- *
+ *
* @param document the DocumentEntry to be read
- *
+ *
* @exception IOException if the DocumentEntry cannot be opened (like, maybe it has
* been deleted?)
*/
@@ -91,7 +91,7 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
DocumentNode doc = (DocumentNode)document;
DocumentProperty property = (DocumentProperty)doc.getProperty();
_document = new POIFSDocument(
- property,
+ property,
((DirectoryNode)doc.getParent()).getFileSystem()
);
_data = _document.getBlockIterator();
@@ -99,7 +99,7 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
/**
* Create an InputStream from the specified Document
- *
+ *
* @param document the Document to be read
*/
public DocumentInputStream(POIFSDocument document) {
@@ -146,7 +146,7 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
}
@Override
- public void mark(int ignoredReadlimit) {
+ public synchronized void mark(int ignoredReadlimit) {
_marked_offset = _current_offset;
_marked_offset_count = Math.max(0, _current_block_count - 1);
}
@@ -159,13 +159,7 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
}
byte[] b = new byte[1];
int result = read(b, 0, 1);
- if(result >= 0) {
- if(b[0] < 0) {
- return b[0]+256;
- }
- return b[0];
- }
- return result;
+ return (result == EOF) ? EOF : (b[0] & 0xFF);
}
@Override
@@ -199,7 +193,7 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
* method repositions the stream to its beginning.
*/
@Override
- public void reset() {
+ public synchronized void reset() {
// Special case for reset to the start
if(_marked_offset == 0 && _marked_offset_count == 0) {
_current_block_count = _marked_offset_count;
@@ -216,15 +210,15 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
_buffer = _data.next();
_current_offset += _buffer.remaining();
}
-
+
_current_block_count = _marked_offset_count;
-
+
// Do we need to position within it?
if(_current_offset != _marked_offset) {
// Grab the right block
_buffer = _data.next();
_current_block_count++;
-
+
// Skip to the right place in it
// (It should be positioned already at the start of the block,
// we need to move further inside the block)
@@ -250,9 +244,9 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
} else if (new_offset > _document_size) {
new_offset = _document_size;
}
-
+
long rval = new_offset - _current_offset;
-
+
// TODO Do this better
byte[] skip = IOUtils.safelyAllocate(rval, Integer.MAX_VALUE);
readFully(skip);
@@ -298,7 +292,7 @@ public final class DocumentInputStream extends InputStream implements LittleEndi
_current_block_count++;
_buffer = _data.next();
}
-
+
int limit = Math.min(len-read, _buffer.remaining());
_buffer.get(buf, off+read, limit);
_current_offset += limit;
diff --git a/src/java/org/apache/poi/sl/draw/DrawFactory.java b/src/java/org/apache/poi/sl/draw/DrawFactory.java
index 6a032d6c6e..6b63274455 100644
--- a/src/java/org/apache/poi/sl/draw/DrawFactory.java
+++ b/src/java/org/apache/poi/sl/draw/DrawFactory.java
@@ -47,17 +47,21 @@ public class DrawFactory {
* This is a fallback, for operations where usercode can't set a graphics context.
* Preferably use the rendering hint {@link Drawable#DRAW_FACTORY} to set the factory.
*
- * @param factory the custom factory
+ * @param factory the custom factory or {@code null} to reset/remove the default factory
*/
@SuppressWarnings("unused")
public static void setDefaultFactory(DrawFactory factory) {
- defaultFactory.set(factory);
+ if (factory == null) {
+ defaultFactory.remove();
+ } else {
+ defaultFactory.set(factory);
+ }
}
/**
* Returns the DrawFactory, preferably via a graphics instance.
* If graphics is null, the current thread local is checked or
- * if it is not set, a new factory is created.
+ * if it is not set, a new factory is created.
*
* @param graphics the current graphics context or null
* @return the draw factory
@@ -112,7 +116,7 @@ public class DrawFactory {
} else if (shape.getClass().isAnnotationPresent(DrawNotImplemented.class)) {
return new DrawNothing(shape);
}
-
+
throw new IllegalArgumentException("Unsupported shape type: "+shape.getClass());
}
@@ -139,11 +143,11 @@ public class DrawFactory {
public DrawConnectorShape getDrawable(ConnectorShape<?,?> shape) {
return new DrawConnectorShape(shape);
}
-
+
public DrawTableShape getDrawable(TableShape<?,?> shape) {
return new DrawTableShape(shape);
}
-
+
public DrawTextShape getDrawable(TextShape<?,?> shape) {
return new DrawTextShape(shape);
}
@@ -151,15 +155,15 @@ public class DrawFactory {
public DrawGroupShape getDrawable(GroupShape<?,?> shape) {
return new DrawGroupShape(shape);
}
-
+
public DrawPictureShape getDrawable(PictureShape<?,?> shape) {
return new DrawPictureShape(shape);
}
-
+
public DrawGraphicalFrame getDrawable(GraphicalFrame<?,?> shape) {
return new DrawGraphicalFrame(shape);
}
-
+
public DrawTextParagraph getDrawable(TextParagraph<?,?,?> paragraph) {
return new DrawTextParagraph(paragraph);
}
@@ -167,12 +171,12 @@ public class DrawFactory {
public DrawBackground getDrawable(Background<?,?> shape) {
return new DrawBackground(shape);
}
-
+
@SuppressWarnings("WeakerAccess")
public DrawTextFragment getTextFragment(TextLayout layout, AttributedString str) {
return new DrawTextFragment(layout, str);
}
-
+
public DrawPaint getPaint(PlaceableShape<?,?> shape) {
return new DrawPaint(shape);
}
@@ -183,7 +187,7 @@ public class DrawFactory {
*
* @param graphics the graphics context to draw to
* @param shape the shape
- * @param bounds the bounds within the graphics context to draw to
+ * @param bounds the bounds within the graphics context to draw to
*/
public void drawShape(Graphics2D graphics, Shape<?,?> shape, Rectangle2D bounds) {
Rectangle2D shapeBounds = shape.getAnchor();
@@ -202,7 +206,7 @@ public class DrawFactory {
tx.translate(-shapeBounds.getCenterX(), -shapeBounds.getCenterY());
}
graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, tx);
-
+
Drawable d = getDrawable(shape);
d.applyTransform(graphics);
d.draw(graphics);
@@ -210,7 +214,7 @@ public class DrawFactory {
graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, txg);
}
}
-
+
/**
* Return a FontManager, either registered beforehand or a default implementation
diff --git a/src/java/org/apache/poi/sl/draw/DrawShape.java b/src/java/org/apache/poi/sl/draw/DrawShape.java
index 67162ac4a8..28ac58b011 100644
--- a/src/java/org/apache/poi/sl/draw/DrawShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawShape.java
@@ -60,56 +60,52 @@ public class DrawShape implements Drawable {
return;
}
- final PlaceableShape<?,?> ps = (PlaceableShape<?,?>)shape;
- final boolean isHSLF = isHSLF(shape);
+ final Rectangle2D anchor = getAnchor(graphics, (PlaceableShape<?,?>)shape);
+ if (anchor == null) {
+ return;
+ }
- final Rectangle2D anchor = getAnchor(graphics, ps);
-
- char[] cmds = isHSLF ? new char[]{'h', 'v', 'r'} : new char[]{'r', 'h', 'v'};
- for (char ch : cmds) {
- switch (ch) {
- case 'h':
- //flip horizontal
- if (ps.getFlipHorizontal()) {
- graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
- graphics.scale(-1, 1);
- graphics.translate(-anchor.getX(), -anchor.getY());
- }
- break;
- case 'v':
- //flip vertical
- if (ps.getFlipVertical()) {
- graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
- graphics.scale(1, -1);
- graphics.translate(-anchor.getX(), -anchor.getY());
- }
- break;
- case 'r':
- // rotation
- double rotation = ps.getRotation();
- if (rotation != 0.) {
- // PowerPoint rotates shapes relative to the geometric center
- double centerX = anchor.getCenterX();
- double centerY = anchor.getCenterY();
-
-
- // transformation is applied reversed ...
- graphics.translate(centerX, centerY);
- graphics.rotate(Math.toRadians(rotation));
- graphics.translate(-centerX, -centerY);
- }
- break;
- default:
- throw new RuntimeException("unexpected transform code " + ch);
- }
+ if (isHSLF(shape)) {
+ flipHorizontal(graphics, anchor);
+ flipVertical(graphics, anchor);
+ rotate(graphics, anchor);
+ } else {
+ rotate(graphics, anchor);
+ flipHorizontal(graphics, anchor);
+ flipVertical(graphics, anchor);
}
}
- private static double safeScale(double dim1, double dim2) {
- if (dim1 == 0.) {
- return 1;
+ private void flipHorizontal(Graphics2D graphics, Rectangle2D anchor) {
+ assert(shape instanceof PlaceableShape && anchor != null);
+ if (((PlaceableShape<?,?>)shape).getFlipHorizontal()) {
+ graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
+ graphics.scale(-1, 1);
+ graphics.translate(-anchor.getX(), -anchor.getY());
}
- return (dim2 == 0.) ? 1 : dim1/dim2;
+ }
+
+ private void flipVertical(Graphics2D graphics, Rectangle2D anchor) {
+ assert(shape instanceof PlaceableShape && anchor != null);
+ if (((PlaceableShape<?,?>)shape).getFlipVertical()) {
+ graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
+ graphics.scale(1, -1);
+ graphics.translate(-anchor.getX(), -anchor.getY());
+ }
+ }
+
+ private void rotate(Graphics2D graphics, Rectangle2D anchor) {
+ assert(shape instanceof PlaceableShape && anchor != null);
+ double rotation = ((PlaceableShape<?,?>)shape).getRotation();
+ if (rotation != 0.) {
+ // PowerPoint rotates shapes relative to the geometric center
+ graphics.rotate(Math.toRadians(rotation), anchor.getCenterX(), anchor.getCenterY());
+ }
+
+ }
+
+ private static double safeScale(double dim1, double dim2) {
+ return (dim1 == 0. || dim2 == 0.) ? 1 : dim1/dim2;
}
@Override
@@ -160,15 +156,11 @@ public class DrawShape implements Drawable {
// this handling is only based on try and error ... not sure why h/xslf is handled differently.
if (!isHSLF) {
- txs2.translate(centerX, centerY);
- txs2.quadrantRotate(1);
- txs2.translate(-centerX, -centerY);
+ txs2.quadrantRotate(1, centerX, centerY);
txs2.concatenate(tx);
}
- txs2.translate(centerX, centerY);
- txs2.quadrantRotate(3);
- txs2.translate(-centerX, -centerY);
+ txs2.quadrantRotate(3, centerX, centerY);
if (isHSLF) {
txs2.concatenate(tx);
diff --git a/src/java/org/apache/poi/ss/formula/functions/Delta.java b/src/java/org/apache/poi/ss/formula/functions/Delta.java
index 0ad65c375e..c26ca2755c 100644
--- a/src/java/org/apache/poi/ss/formula/functions/Delta.java
+++ b/src/java/org/apache/poi/ss/formula/functions/Delta.java
@@ -18,9 +18,11 @@
package org.apache.poi.ss.formula.functions;
import org.apache.poi.ss.formula.OperationEvaluationContext;
-import org.apache.poi.ss.formula.eval.*;
-
-import java.math.BigDecimal;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.EvaluationException;
+import org.apache.poi.ss.formula.eval.NumberEval;
+import org.apache.poi.ss.formula.eval.OperandResolver;
+import org.apache.poi.ss.formula.eval.ValueEval;
/**
* Implementation for Excel DELTA() function.<p>
@@ -46,33 +48,20 @@ public final class Delta extends Fixed2ArgFunction implements FreeRefFunction {
private final static NumberEval ZERO = new NumberEval(0);
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2) {
- ValueEval veText1;
try {
- veText1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex);
- } catch (EvaluationException e) {
- return e.getErrorEval();
- }
- String strText1 = OperandResolver.coerceValueToString(veText1);
- Double number1 = OperandResolver.parseDouble(strText1);
- if (number1 == null) {
- return ErrorEval.VALUE_INVALID;
- }
+ Double number1 = evaluateValue(arg1, srcRowIndex, srcColumnIndex);
+ if (number1 == null) {
+ return ErrorEval.VALUE_INVALID;
+ }
+ Double number2 = evaluateValue(arg2, srcRowIndex, srcColumnIndex);
+ if (number2 == null) {
+ return ErrorEval.VALUE_INVALID;
+ }
- ValueEval veText2;
- try {
- veText2 = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex);
+ return (number1.compareTo(number2) == 0) ? ONE : ZERO;
} catch (EvaluationException e) {
return e.getErrorEval();
}
-
- String strText2 = OperandResolver.coerceValueToString(veText2);
- Double number2 = OperandResolver.parseDouble(strText2);
- if (number2 == null) {
- return ErrorEval.VALUE_INVALID;
- }
-
- int result = new BigDecimal(number1.doubleValue()).compareTo(new BigDecimal(number2.doubleValue()));
- return result == 0 ? ONE : ZERO;
}
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
@@ -82,4 +71,10 @@ public final class Delta extends Fixed2ArgFunction implements FreeRefFunction {
return ErrorEval.VALUE_INVALID;
}
+
+ private static Double evaluateValue(ValueEval arg, int srcRowIndex, int srcColumnIndex) throws EvaluationException {
+ ValueEval veText = OperandResolver.getSingleValue(arg, srcRowIndex, srcColumnIndex);
+ String strText1 = OperandResolver.coerceValueToString(veText);
+ return OperandResolver.parseDouble(strText1);
+ }
} \ No newline at end of file
diff --git a/src/java/org/apache/poi/ss/formula/functions/Fixed.java b/src/java/org/apache/poi/ss/formula/functions/Fixed.java
index e18bb062ea..364a05824c 100644
--- a/src/java/org/apache/poi/ss/formula/functions/Fixed.java
+++ b/src/java/org/apache/poi/ss/formula/functions/Fixed.java
@@ -64,7 +64,8 @@ public final class Fixed implements Function1Arg, Function2Arg, Function3Arg {
}
return ErrorEval.VALUE_INVALID;
}
-
+
+ @SuppressWarnings("squid:S2111")
private ValueEval fixed(
ValueEval numberParam, ValueEval placesParam,
ValueEval skipThousandsSeparatorParam,
@@ -85,16 +86,16 @@ public final class Fixed implements Function1Arg, Function2Arg, Function3Arg {
Boolean skipThousandsSeparator =
OperandResolver.coerceValueToBoolean(
skipThousandsSeparatorValueEval, false);
-
+
// Round number to respective places.
number = number.setScale(places, RoundingMode.HALF_UP);
-
+
// Format number conditionally using a thousands separator.
NumberFormat nf = NumberFormat.getNumberInstance(Locale.US);
DecimalFormat formatter = (DecimalFormat)nf;
formatter.setGroupingUsed(!(skipThousandsSeparator != null && skipThousandsSeparator));
- formatter.setMinimumFractionDigits(places >= 0 ? places : 0);
- formatter.setMaximumFractionDigits(places >= 0 ? places : 0);
+ formatter.setMinimumFractionDigits(Math.max(places, 0));
+ formatter.setMaximumFractionDigits(Math.max(places, 0));
String numberString = formatter.format(number.doubleValue());
// Return the result as a StringEval.
diff --git a/src/java/org/apache/poi/ss/formula/functions/Irr.java b/src/java/org/apache/poi/ss/formula/functions/Irr.java
index 5d91be9a74..7430335a0a 100644
--- a/src/java/org/apache/poi/ss/formula/functions/Irr.java
+++ b/src/java/org/apache/poi/ss/formula/functions/Irr.java
@@ -31,6 +31,8 @@ import org.apache.poi.ss.formula.eval.ValueEval;
* @see <a href="http://office.microsoft.com/en-us/excel-help/irr-HP005209146.aspx">Excel IRR</a>
*/
public final class Irr implements Function {
+ private static final int MAX_ITERATION_COUNT = 20;
+ private static final double ABSOLUTE_ACCURACY = 1E-7;
public ValueEval evaluate(final ValueEval[] args, final int srcRowIndex, final int srcColumnIndex) {
@@ -89,27 +91,24 @@ public final class Irr implements Function {
* http://en.wikipedia.org/wiki/Newton%27s_method</a>
*/
public static double irr(double[] values, double guess) {
- final int maxIterationCount = 20;
- final double absoluteAccuracy = 1E-7;
double x0 = guess;
- double x1;
- int i = 0;
- while (i < maxIterationCount) {
+ for (int i = 0; i < MAX_ITERATION_COUNT; i++) {
// the value of the function (NPV) and its derivate can be calculated in the same loop
final double factor = 1.0 + x0;
- int k = 0;
- double fValue = values[k];
+ double denominator = factor;
+ if (denominator == 0) {
+ return Double.NaN;
+ }
+
+ double fValue = values[0];
double fDerivative = 0;
- for (double denominator = factor; ++k < values.length; ) {
+ for (int k = 1; k < values.length; k++) {
final double value = values[k];
fValue += value / denominator;
denominator *= factor;
- if (denominator == 0) {
- return Double.NaN;
- }
fDerivative -= k * value / denominator;
}
@@ -117,14 +116,13 @@ public final class Irr implements Function {
if (fDerivative == 0) {
return Double.NaN;
}
- x1 = x0 - fValue/fDerivative;
+ double x1 = x0 - fValue/fDerivative;
- if (Math.abs(x1 - x0) <= absoluteAccuracy) {
+ if (Math.abs(x1 - x0) <= ABSOLUTE_ACCURACY) {
return x1;
}
x0 = x1;
- ++i;
}
// maximum number of iterations is exceeded
return Double.NaN;
diff --git a/src/java/org/apache/poi/ss/usermodel/DataFormatter.java b/src/java/org/apache/poi/ss/usermodel/DataFormatter.java
index 15bbef0fbe..19c0afaa7c 100644
--- a/src/java/org/apache/poi/ss/usermodel/DataFormatter.java
+++ b/src/java/org/apache/poi/ss/usermodel/DataFormatter.java
@@ -16,7 +16,7 @@
2012 - Alfresco Software, Ltd.
Alfresco Software has modified source of this file
- The details of changes as svn diff can be found in svn at location root/projects/3rd-party/src
+ The details of changes as svn diff can be found in svn at location root/projects/3rd-party/src
==================================================================== */
package org.apache.poi.ss.usermodel;
@@ -111,12 +111,12 @@ import org.apache.poi.util.POILogger;
* <p>
* Some formats are automatically "localized" by Excel, eg show as mm/dd/yyyy when
* loaded in Excel in some Locales but as dd/mm/yyyy in others. These are always
- * returned in the "default" (US) format, as stored in the file.
- * Some format strings request an alternate locale, eg
+ * returned in the "default" (US) format, as stored in the file.
+ * Some format strings request an alternate locale, eg
* <code>[$-809]d/m/yy h:mm AM/PM</code> which explicitly requests UK locale.
* These locale directives are (currently) ignored.
* You can use {@link DateFormatConverter} to do some of this localisation if
- * you need it.
+ * you need it.
*/
public class DataFormatter implements Observer {
private static final String defaultFractionWholePartFormat = "#";
@@ -129,13 +129,13 @@ public class DataFormatter implements Observer {
/** Pattern to find "AM/PM" marker */
private static final Pattern amPmPattern = Pattern.compile("(([AP])[M/P]*)", Pattern.CASE_INSENSITIVE);
-
+
/** Pattern to find formats with condition ranges e.g. [>=100] */
private static final Pattern rangeConditionalPattern = Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*");
- /**
+ /**
* A regex to find locale patterns like [$$-1009] and [$?-452].
- * Note that we don't currently process these into locales
+ * Note that we don't currently process these into locales
*/
private static final Pattern localePatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+])");
@@ -144,14 +144,14 @@ public class DataFormatter implements Observer {
* Allowed colours are: Black, Blue, Cyan, Green,
* Magenta, Red, White, Yellow, "Color n" (1<=n<=56)
*/
- private static final Pattern colorPattern =
+ private static final Pattern colorPattern =
Pattern.compile("(\\[BLACK])|(\\[BLUE])|(\\[CYAN])|(\\[GREEN])|" +
"(\\[MAGENTA])|(\\[RED])|(\\[WHITE])|(\\[YELLOW])|" +
"(\\[COLOR\\s*\\d])|(\\[COLOR\\s*[0-5]\\d])", Pattern.CASE_INSENSITIVE);
/**
* A regex to identify a fraction pattern.
- * This requires that replaceAll("\\?", "#") has already been called
+ * This requires that replaceAll("\\?", "#") has already been called
*/
private static final Pattern fractionPattern = Pattern.compile("(?:([#\\d]+)\\s+)?(#+)\\s*/\\s*([#\\d]+)");
@@ -162,10 +162,10 @@ public class DataFormatter implements Observer {
/**
* A regex to detect if an alternate grouping character is used
- * in a numeric format
+ * in a numeric format
*/
private static final Pattern alternateGrouping = Pattern.compile("([#0]([^.#0])[#0]{3})");
-
+
/**
* Cells formatted with a date or time format and which contain invalid date or time values
* show 255 pound signs ("#").
@@ -191,7 +191,7 @@ public class DataFormatter implements Observer {
* A default date format, if no date format was given
*/
private DateFormat defaultDateformat;
-
+
/** <em>General</em> format for numbers. */
private Format generalNumberFormat;
@@ -208,10 +208,10 @@ public class DataFormatter implements Observer {
/** stores the locale valid it the last formatting call */
private Locale locale;
-
+
/** stores if the locale should change according to {@link LocaleUtil#getUserLocale()} */
private boolean localeIsAdapting;
-
+
private class LocaleChangeObservable extends Observable {
void checkForLocaleChange() {
checkForLocaleChange(LocaleUtil.getUserLocale());
@@ -223,13 +223,13 @@ public class DataFormatter implements Observer {
notifyObservers(newLocale);
}
}
-
+
/** the Observable to notify, when the locale has been changed */
private final LocaleChangeObservable localeChangedObservable = new LocaleChangeObservable();
-
+
/** For logging any problems we find */
private static POILogger logger = POILogFactory.getLogger(DataFormatter.class);
-
+
/**
* Creates a formatter using the {@link Locale#getDefault() default locale}.
*/
@@ -281,7 +281,7 @@ public class DataFormatter implements Observer {
/**
* Return a Format for the given cell if one exists, otherwise try to
- * create one. This method will return <code>null</code> if the any of the
+ * create one. This method will return <code>null</code> if any of the
* following is true:
* <ul>
* <li>the cell's style is null</li>
@@ -294,9 +294,9 @@ public class DataFormatter implements Observer {
*/
private Format getFormat(Cell cell, ConditionalFormattingEvaluator cfEvaluator) {
if (cell == null) return null;
-
+
ExcelNumberFormat numFmt = ExcelNumberFormat.from(cell, cfEvaluator);
-
+
if ( numFmt == null) {
return null;
}
@@ -316,25 +316,25 @@ public class DataFormatter implements Observer {
}
return false;
}
-
+
private Format getFormat(double cellValue, int formatIndex, String formatStrIn, boolean use1904Windowing) {
localeChangedObservable.checkForLocaleChange();
// Might be better to separate out the n p and z formats, falling back to p when n and z are not set.
// That however would require other code to be re factored.
// String[] formatBits = formatStrIn.split(";");
- // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2;
+ // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2;
// String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0];
String formatStr = formatStrIn;
-
+
// Excel supports 2+ part conditional data formats, eg positive/negative/zero,
// or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds
- // of different formats for different ranges, just +ve/-ve, we need to
+ // of different formats for different ranges, just +ve/-ve, we need to
// handle these ourselves in a special way.
// For now, if we detect 2+ parts, we call out to CellFormat to handle it
// TODO Going forward, we should really merge the logic between the two classes
- if (formatStr.contains(";") &&
+ if (formatStr.contains(";") &&
(formatStr.indexOf(';') != formatStr.lastIndexOf(';')
|| rangeConditionalPattern.matcher(formatStr).matches()
) ) {
@@ -343,8 +343,8 @@ public class DataFormatter implements Observer {
CellFormat cfmt = CellFormat.getInstance(locale, formatStr);
// CellFormat requires callers to identify date vs not, so do so
Object cellValueO = Double.valueOf(cellValue);
- if (DateUtil.isADateFormat(formatIndex, formatStr) &&
- // don't try to handle Date value 0, let a 3 or 4-part format take care of it
+ if (DateUtil.isADateFormat(formatIndex, formatStr) &&
+ // don't try to handle Date value 0, let a 3 or 4-part format take care of it
((Double)cellValueO).doubleValue() != 0.0) {
cellValueO = DateUtil.getJavaDate(cellValue, use1904Windowing);
}
@@ -354,23 +354,23 @@ public class DataFormatter implements Observer {
logger.log(POILogger.WARN, "Formatting failed for format " + formatStr + ", falling back", e);
}
}
-
+
// Excel's # with value 0 will output empty where Java will output 0. This hack removes the # from the format.
if (emulateCSV && cellValue == 0.0 && formatStr.contains("#") && !formatStr.contains("0")) {
formatStr = formatStr.replaceAll("#", "");
}
-
+
// See if we already have it cached
Format format = formats.get(formatStr);
if (format != null) {
return format;
}
-
+
// Is it one of the special built in types, General or @?
if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
return generalNumberFormat;
}
-
+
// Build a formatter, and cache it
format = createFormat(cellValue, formatIndex, formatStr);
formats.put(formatStr, format);
@@ -393,14 +393,14 @@ public class DataFormatter implements Observer {
private Format createFormat(double cellValue, int formatIndex, String sFormat) {
localeChangedObservable.checkForLocaleChange();
-
+
String formatStr = sFormat;
-
+
// Remove colour formatting if present
Matcher colourM = colorPattern.matcher(formatStr);
while(colourM.find()) {
String colour = colourM.group();
-
+
// Paranoid replacement...
int at = formatStr.indexOf(colour);
if(at == -1) break;
@@ -431,7 +431,7 @@ public class DataFormatter implements Observer {
if(formatStr == null || formatStr.trim().length() == 0) {
return getDefaultFormat(cellValue);
}
-
+
if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) {
return generalNumberFormat;
}
@@ -455,12 +455,12 @@ public class DataFormatter implements Observer {
return new FractionFormat(wholePart, fractionMatcher.group(3));
}
}
-
+
// Strip custom text in quotes and escaped characters for now as it can cause performance problems in fractions.
//String strippedFormatStr = formatStr.replaceAll("\\\\ ", " ").replaceAll("\\\\.", "").replaceAll("\"[^\"]*\"", " ").replaceAll("\\?", "#");
return new FractionFormat(defaultFractionWholePartFormat, defaultFractionFractionPartFormat);
}
-
+
if (numPattern.matcher(formatStr).find()) {
return createNumberFormat(formatStr, cellValue);
}
@@ -471,8 +471,8 @@ public class DataFormatter implements Observer {
// TODO - when does this occur?
return null;
}
-
-
+
+
private Format createDateFormat(String pFormatStr, double cellValue) {
String formatStr = pFormatStr;
@@ -480,7 +480,7 @@ public class DataFormatter implements Observer {
formatStr = formatStr.replaceAll("\\\\,",",");
formatStr = formatStr.replaceAll("\\\\\\.","."); // . is a special regexp char
formatStr = formatStr.replaceAll("\\\\ "," ");
- formatStr = formatStr.replaceAll("\\\\/","/"); // weird: m\\/d\\/yyyy
+ formatStr = formatStr.replaceAll("\\\\/","/"); // weird: m\\/d\\/yyyy
formatStr = formatStr.replaceAll(";@", "");
formatStr = formatStr.replaceAll("\"/\"", "/"); // "/" is escaped for no reason in: mm"/"dd"/"yyyy
formatStr = formatStr.replace("\"\"", "'"); // replace Excel quoting with Java style quoting
@@ -675,7 +675,7 @@ public class DataFormatter implements Observer {
}
}
- // Now, handle the other aspects like
+ // Now, handle the other aspects like
// quoting and scientific notation
for(int i = 0; i < sb.length(); i++) {
char c = sb.charAt(i);
@@ -748,7 +748,7 @@ public class DataFormatter implements Observer {
private Format createNumberFormat(String formatStr, double cellValue) {
String format = cleanFormatForNumber(formatStr);
DecimalFormatSymbols symbols = decimalSymbols;
-
+
// Do we need to change the grouping character?
// eg for a format like #'##0 which wants 12'345 not 12,345
Matcher agm = alternateGrouping.matcher(format);
@@ -766,7 +766,7 @@ public class DataFormatter implements Observer {
format = format.replace(oldPart, newPart);
}
}
-
+
try {
return new InternalDecimalFormatWithScale(format, symbols);
} catch(IllegalArgumentException iae) {
@@ -787,7 +787,7 @@ public class DataFormatter implements Observer {
}
private Format getDefaultFormat(double cellValue) {
localeChangedObservable.checkForLocaleChange();
-
+
// for numeric cells try user supplied default
if (defaultNumFormat != null) {
return defaultNumFormat;
@@ -826,6 +826,16 @@ public class DataFormatter implements Observer {
return null;
}
Format dateFormat = getFormat(cell, cfEvaluator);
+ if (dateFormat == null) {
+ if (defaultDateformat == null) {
+ DateFormatSymbols sym = DateFormatSymbols.getInstance(LocaleUtil.getUserLocale());
+ SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", sym);
+ sdf.setTimeZone(LocaleUtil.getUserTimeZone());
+ dateFormat = sdf;
+ } else {
+ dateFormat = defaultNumFormat;
+ }
+ }
synchronized (dateFormat) {
if(dateFormat instanceof ExcelStyleDateFormatter) {
// Hint about the raw excel value
@@ -846,7 +856,7 @@ public class DataFormatter implements Observer {
* <p>
* Format comes from either the highest priority conditional format rule with a
* specified format, or from the cell style.
- *
+ *
* @param cell The cell
* @param cfEvaluator if available, or null
* @return a formatted number string
@@ -879,7 +889,7 @@ public class DataFormatter implements Observer {
*/
public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) {
localeChangedObservable.checkForLocaleChange();
-
+
// Is it a date?
if(DateUtil.isADateFormat(formatIndex,formatString)) {
if(DateUtil.isValidExcelDate(value)) {
@@ -896,13 +906,13 @@ public class DataFormatter implements Observer {
return invalidDateTimeString;
}
}
-
+
// else Number
Format numberFormat = getFormat(value, formatIndex, formatString, use1904Windowing);
if (numberFormat == null) {
return String.valueOf(value);
}
-
+
// When formatting 'value', double to text to BigDecimal produces more
// accurate results than double to Double in JDK8 (as compared to
// previous versions). However, if the value contains E notation, this
@@ -960,7 +970,7 @@ public class DataFormatter implements Observer {
public String formatCellValue(Cell cell, FormulaEvaluator evaluator) {
return formatCellValue(cell, evaluator, null);
}
-
+
/**
* <p>
* Returns the formatted value of a cell as a <tt>String</tt> regardless
@@ -990,7 +1000,7 @@ public class DataFormatter implements Observer {
*/
public String formatCellValue(Cell cell, FormulaEvaluator evaluator, ConditionalFormattingEvaluator cfEvaluator) {
localeChangedObservable.checkForLocaleChange();
-
+
if (cell == null) {
return "";
}
@@ -1077,9 +1087,9 @@ public class DataFormatter implements Observer {
result.setParseIntegerOnly(true);
return result;
}
-
+
/**
- * Enables excel style rounding mode (round half up) on the
+ * Enables excel style rounding mode (round half up) on the
* Decimal Format given.
*/
public static void setExcelStyleRoundingMode(DecimalFormat format) {
@@ -1119,16 +1129,16 @@ public class DataFormatter implements Observer {
if (!(localeObj instanceof Locale)) return;
Locale newLocale = (Locale)localeObj;
if (!localeIsAdapting || newLocale.equals(locale)) return;
-
+
locale = newLocale;
-
+
dateSymbols = DateFormatSymbols.getInstance(locale);
decimalSymbols = DecimalFormatSymbols.getInstance(locale);
generalNumberFormat = new ExcelGeneralNumberFormat(locale);
// taken from Date.toString()
defaultDateformat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", dateSymbols);
- defaultDateformat.setTimeZone(LocaleUtil.getUserTimeZone());
+ defaultDateformat.setTimeZone(LocaleUtil.getUserTimeZone());
// init built-in formats
@@ -1262,10 +1272,10 @@ public class DataFormatter implements Observer {
return df.parseObject(source, pos);
}
}
-
-
-
+
+
+
/**
* Format class that does nothing and always returns a constant string.
*
@@ -1294,7 +1304,7 @@ public class DataFormatter implements Observer {
}
/**
* Workaround until we merge {@link DataFormatter} with {@link CellFormat}.
- * Constant, non-cachable wrapper around a {@link CellFormatResult}
+ * Constant, non-cachable wrapper around a {@link CellFormatResult}
*/
@SuppressWarnings("serial")
private final class CellFormatResultWrapper extends Format {
diff --git a/src/java/org/apache/poi/ss/usermodel/ExcelGeneralNumberFormat.java b/src/java/org/apache/poi/ss/usermodel/ExcelGeneralNumberFormat.java
index 2efbb3ff03..891c63e8d1 100644
--- a/src/java/org/apache/poi/ss/usermodel/ExcelGeneralNumberFormat.java
+++ b/src/java/org/apache/poi/ss/usermodel/ExcelGeneralNumberFormat.java
@@ -16,7 +16,7 @@
2012 - Alfresco Software, Ltd.
Alfresco Software has modified source of this file
- The details of changes as svn diff can be found in svn at location root/projects/3rd-party/src
+ The details of changes as svn diff can be found in svn at location root/projects/3rd-party/src
==================================================================== */
package org.apache.poi.ss.usermodel;
@@ -55,6 +55,7 @@ public class ExcelGeneralNumberFormat extends Format {
DataFormatter.setExcelStyleRoundingMode(decimalFormat);
}
+ @SuppressWarnings("squid:S2111")
public StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition pos) {
final double value;
if (number instanceof Number) {
diff --git a/src/java/org/apache/poi/ss/usermodel/FractionFormat.java b/src/java/org/apache/poi/ss/usermodel/FractionFormat.java
index 66f5e7c535..eb6fa7bf6b 100644
--- a/src/java/org/apache/poi/ss/usermodel/FractionFormat.java
+++ b/src/java/org/apache/poi/ss/usermodel/FractionFormat.java
@@ -31,11 +31,11 @@ import org.apache.poi.util.POILogger;
/**
* <p>Format class that handles Excel style fractions, such as "# #/#" and "#/###"</p>
- *
+ *
* <p>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.</p>
- *
+ *
* <p>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.</p>
@@ -43,7 +43,7 @@ import org.apache.poi.util.POILogger;
@SuppressWarnings("serial")
public class FractionFormat extends Format {
- private static final POILogger LOGGER = POILogFactory.getLogger(FractionFormat.class);
+ 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
@@ -78,7 +78,7 @@ public class FractionFormat extends Format {
try{
tmpExact = Integer.parseInt(m.group(2));
//if the denom is 0, fall back to the default: tmpExact=100
-
+
if (tmpExact == 0){
tmpExact = -1;
}
@@ -104,10 +104,11 @@ public class FractionFormat extends Format {
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();
@@ -117,7 +118,7 @@ public class FractionFormat extends Format {
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
@@ -125,10 +126,10 @@ public class FractionFormat extends Format {
// 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("-");
@@ -136,7 +137,7 @@ public class FractionFormat extends Format {
sb.append(wholePart);
return sb.toString();
}
-
+
final SimpleFraction fract;
try {
//this should be the case because of the constructor
@@ -151,12 +152,12 @@ public class FractionFormat extends Format {
}
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();
@@ -165,8 +166,8 @@ public class FractionFormat extends Format {
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);
@@ -190,5 +191,5 @@ public class FractionFormat extends Format {
public Object parseObject(String source, ParsePosition pos) {
throw new NotImplementedException("Reverse parsing not supported");
}
-
+
}