mirror of https://github.com/apache/poi.git
Sonar fixes - type: bugs / severity: major
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1871064 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
82156bc63c
commit
0f29ae8e4d
|
@ -685,8 +685,8 @@ public class AddDimensionedImage {
|
|||
if(sheet instanceof HSSFSheet) {
|
||||
// Next, from the columns width, calculate how many co-ordinate
|
||||
// positons there are per millimetre
|
||||
coordinatePositionsPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /
|
||||
colWidthMM;
|
||||
coordinatePositionsPerMM = (colWidthMM == 0) ? 0
|
||||
: ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS / colWidthMM;
|
||||
// From this figure, determine how many co-ordinat positions to
|
||||
// inset the left hand or bottom edge of the image.
|
||||
inset = (int)(coordinatePositionsPerMM * overlapMM);
|
||||
|
@ -784,8 +784,8 @@ public class AddDimensionedImage {
|
|||
}
|
||||
|
||||
if(sheet instanceof HSSFSheet) {
|
||||
rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /
|
||||
rowHeightMM;
|
||||
rowCoordinatesPerMM = (rowHeightMM == 0) ? 0
|
||||
: ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS / rowHeightMM;
|
||||
inset = (int)(overlapMM * rowCoordinatesPerMM);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -17,15 +17,26 @@
|
|||
|
||||
package org.apache.poi.ss.examples;
|
||||
|
||||
import org.apache.poi.xssf.usermodel.*;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Calendar;
|
||||
import java.io.FileOutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.BorderStyle;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.DataFormat;
|
||||
import org.apache.poi.ss.usermodel.FillPatternType;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
import org.apache.poi.ss.usermodel.PrintSetup;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
/**
|
||||
* A business plan demo
|
||||
|
@ -36,8 +47,6 @@ import java.text.SimpleDateFormat;
|
|||
*/
|
||||
public class BusinessPlan {
|
||||
|
||||
private static SimpleDateFormat fmt = new SimpleDateFormat("dd-MMM");
|
||||
|
||||
private static final String[] titles = {
|
||||
"ID", "Project Name", "Owner", "Days", "Start", "End"};
|
||||
|
||||
|
@ -84,6 +93,8 @@ public class BusinessPlan {
|
|||
if(args.length > 0 && args[0].equals("-xls")) wb = new HSSFWorkbook();
|
||||
else wb = new XSSFWorkbook();
|
||||
|
||||
final SimpleDateFormat fmt = new SimpleDateFormat("dd-MMM");
|
||||
|
||||
Map<String, CellStyle> styles = createStyles(wb);
|
||||
|
||||
Sheet sheet = wb.createSheet("Business Plan");
|
||||
|
@ -201,7 +212,7 @@ public class BusinessPlan {
|
|||
FileOutputStream out = new FileOutputStream(file);
|
||||
wb.write(out);
|
||||
out.close();
|
||||
|
||||
|
||||
wb.close();
|
||||
}
|
||||
|
||||
|
@ -315,7 +326,7 @@ public class BusinessPlan {
|
|||
private static CellStyle createBorderedStyle(Workbook wb){
|
||||
BorderStyle thin = BorderStyle.THIN;
|
||||
short black = IndexedColors.BLACK.getIndex();
|
||||
|
||||
|
||||
CellStyle style = wb.createCellStyle();
|
||||
style.setBorderRight(thin);
|
||||
style.setRightBorderColor(black);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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 < 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
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;
|
||||
}
|
||||
|
||||
return (number1.compareTo(number2) == 0) ? ONE : ZERO;
|
||||
} catch (EvaluationException e) {
|
||||
return e.getErrorEval();
|
||||
}
|
||||
String strText1 = OperandResolver.coerceValueToString(veText1);
|
||||
Double number1 = OperandResolver.parseDouble(strText1);
|
||||
if (number1 == null) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
|
||||
ValueEval veText2;
|
||||
try {
|
||||
veText2 = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex);
|
||||
} 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);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -820,7 +820,7 @@ public class HemfPlusDraw {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("squid:S2111")
|
||||
static double round10(double d) {
|
||||
return new BigDecimal(d).setScale(10, RoundingMode.HALF_UP).doubleValue();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue