diff options
author | Andreas Beeker <kiwiwings@apache.org> | 2018-11-02 18:35:34 +0000 |
---|---|---|
committer | Andreas Beeker <kiwiwings@apache.org> | 2018-11-02 18:35:34 +0000 |
commit | 0e68ef5f843ab6052faae2cb2d60b2575f94d805 (patch) | |
tree | 60e7c2c57afd09761eb549861a5ce2ae2cf48650 /src | |
parent | 8b3974f9452388acf0fa65ec3bad316ec955883c (diff) | |
parent | 9aabade3f067b0befbc2680489ace13a6d5fa7e6 (diff) | |
download | poi-0e68ef5f843ab6052faae2cb2d60b2575f94d805.tar.gz poi-0e68ef5f843ab6052faae2cb2d60b2575f94d805.zip |
merge trunk
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1845617 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
75 files changed, 2410 insertions, 1209 deletions
diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/AddDimensionedImage.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/AddDimensionedImage.java index bee33a250d..480662554d 100644 --- a/src/examples/src/org/apache/poi/hssf/usermodel/examples/AddDimensionedImage.java +++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/AddDimensionedImage.java @@ -142,13 +142,13 @@ import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType; public class AddDimensionedImage { // Four constants that determine how - and indeed whether - the rows - // and columns an image may overlie should be expanded to accomodate that + // and columns an image may overlie should be expanded to accommodate that // image. // Passing EXPAND_ROW will result in the height of a row being increased - // to accomodate the image if it is not already larger. The image will + // to accommodate the image if it is not already larger. The image will // be layed across one or more columns. // Passing EXPAND_COLUMN will result in the width of the column being - // increased to accomodate the image if it is not already larger. The image + // increased to accommodate the image if it is not already larger. The image // will be layed across one or many rows. // Passing EXPAND_ROW_AND_COLUMN will result in the height of the row // bing increased along with the width of the column to accomdate the @@ -266,7 +266,7 @@ public class AddDimensionedImage { } // Call methods to calculate how the image and sheet should be - // manipulated to accomodate the image; columns and then rows. + // manipulated to accommodate the image; columns and then rows. colClientAnchorDetail = this.fitImageToColumns(sheet, colNumber, reqImageWidthMM, resizeBehaviour); rowClientAnchorDetail = this.fitImageToRows(sheet, rowNumber, @@ -312,7 +312,7 @@ public class AddDimensionedImage { } /** - * Determines whether the sheets columns should be re-sized to accomodate + * Determines whether the sheets columns should be re-sized to accommodate * the image, adjusts the columns width if necessary and creates then * returns a ClientAnchorDetail object that facilitates construction of * an HSSFClientAnchor that will fix the image on the sheet and establish @@ -348,7 +348,7 @@ public class AddDimensionedImage { colWidthMM = ConvertImageUnits.widthUnits2Millimetres( (short)sheet.getColumnWidth(colNumber)); - // Check that the column's width will accomodate the image at the + // Check that the column's width will accommodate the image at the // required dimension. If the width of the column is LESS than the // required width of the image, decide how the application should // respond - resize the column or overlay the image across one or more @@ -396,7 +396,7 @@ public class AddDimensionedImage { } /** - * Determines whether the sheet's row should be re-sized to accomodate + * Determines whether the sheet's row should be re-sized to accommodate * the image, adjusts the rows height if necessary and creates then * returns a ClientAnchorDetail object that facilitates construction of * an HSSFClientAnchor that will fix the image on the sheet and establish @@ -436,7 +436,7 @@ public class AddDimensionedImage { // Get the row's height in millimetres double rowHeightMM = row.getHeightInPoints() / ConvertImageUnits.POINTS_PER_MILLIMETRE; - // Check that the row's height will accomodate the image at the required + // Check that the row's height will accommodate the image at the required // dimensions. If the height of the row is LESS than the required height // of the image, decide how the application should respond - resize the // row or overlay the image across a series of rows. diff --git a/src/examples/src/org/apache/poi/hssf/usermodel/examples/CellComments.java b/src/examples/src/org/apache/poi/hssf/usermodel/examples/CellComments.java index b14cc53abe..877708bde8 100644 --- a/src/examples/src/org/apache/poi/hssf/usermodel/examples/CellComments.java +++ b/src/examples/src/org/apache/poi/hssf/usermodel/examples/CellComments.java @@ -17,18 +17,22 @@ package org.apache.poi.hssf.usermodel.examples; -import java.io.FileOutputStream; -import java.io.IOException; - -import org.apache.poi.hssf.usermodel.HSSFCell; -import org.apache.poi.hssf.usermodel.HSSFClientAnchor; import org.apache.poi.hssf.usermodel.HSSFComment; -import org.apache.poi.hssf.usermodel.HSSFFont; -import org.apache.poi.hssf.usermodel.HSSFPatriarch; -import org.apache.poi.hssf.usermodel.HSSFRichTextString; -import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.Comment; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.RichTextString; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; + +import java.io.FileOutputStream; +import java.io.IOException; /** * Demonstrates how to work with excel cell comments.<p> @@ -39,46 +43,64 @@ import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined; public class CellComments { public static void main(String[] args) throws IOException { - try (HSSFWorkbook wb = new HSSFWorkbook()) { - HSSFSheet sheet = wb.createSheet("Cell comments in POI HSSF"); + createWorkbook(false, ".xls"); + createWorkbook(true, ".xlsx"); + } + + private static void createWorkbook(boolean xssf, String extension) throws IOException { + try (Workbook wb = WorkbookFactory.create(xssf)) { + Sheet sheet = wb.createSheet("Cell comments in POI " + extension); + CreationHelper creationHelper = wb.getCreationHelper(); // Create the drawing patriarch. This is the top level container for all shapes including cell comments. - HSSFPatriarch patr = sheet.createDrawingPatriarch(); + Drawing<?> patr = sheet.createDrawingPatriarch(); //create a cell in row 3 - HSSFCell cell1 = sheet.createRow(3).createCell(1); - cell1.setCellValue(new HSSFRichTextString("Hello, World")); + Cell cell1 = sheet.createRow(3).createCell(1); + cell1.setCellValue(creationHelper.createRichTextString("Hello, World")); //anchor defines size and position of the comment in worksheet - HSSFComment comment1 = patr.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short) 4, 2, (short) 6, 5)); + ClientAnchor clientAnchor = creationHelper.createClientAnchor(); + clientAnchor.setCol1(4); + clientAnchor.setRow1(2); + clientAnchor.setCol2(6); + clientAnchor.setRow2(5); + Comment comment1 = patr.createCellComment(clientAnchor); // set text in the comment - comment1.setString(new HSSFRichTextString("We can set comments in POI")); + comment1.setString(creationHelper.createRichTextString("We can set comments in POI")); //set comment author. //you can see it in the status bar when moving mouse over the commented cell comment1.setAuthor("Apache Software Foundation"); - // The first way to assign comment to a cell is via HSSFCell.setCellComment method + // The first way to assign comment to a cell is via Cell.setCellComment method cell1.setCellComment(comment1); //create another cell in row 6 - HSSFCell cell2 = sheet.createRow(6).createCell(1); + Cell cell2 = sheet.createRow(6).createCell(1); cell2.setCellValue(36.6); - HSSFComment comment2 = patr.createComment(new HSSFClientAnchor(0, 0, 0, 0, (short) 4, 8, (short) 6, 11)); - //modify background color of the comment - comment2.setFillColor(204, 236, 255); + clientAnchor = creationHelper.createClientAnchor(); + clientAnchor.setCol1(4); + clientAnchor.setRow1(8); + clientAnchor.setCol2(6); + clientAnchor.setRow2(11); + Comment comment2 = patr.createCellComment(clientAnchor); + //modify background color of the comment, only available in HSSF currently + if (wb instanceof HSSFWorkbook) { + ((HSSFComment) comment2).setFillColor(204, 236, 255); + } - HSSFRichTextString string = new HSSFRichTextString("Normal body temperature"); + RichTextString string = creationHelper.createRichTextString("Normal body temperature"); //apply custom font to the text in the comment - HSSFFont font = wb.createFont(); + Font font = wb.createFont(); font.setFontName("Arial"); font.setFontHeightInPoints((short) 10); font.setBold(true); - font.setColor(HSSFColorPredefined.RED.getIndex()); + font.setColor(IndexedColors.RED.getIndex()); string.applyFont(font); comment2.setString(string); @@ -94,7 +116,7 @@ public class CellComments { comment2.setRow(6); comment2.setColumn(1); - try (FileOutputStream out = new FileOutputStream("poi_comment.xls")) { + try (FileOutputStream out = new FileOutputStream("poi_comment" + extension)) { wb.write(out); } } diff --git a/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java b/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java index 177d0a6d97..fa5e233cf9 100644 --- a/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java +++ b/src/examples/src/org/apache/poi/ss/examples/AddDimensionedImage.java @@ -202,13 +202,13 @@ import org.apache.poi.util.IOUtils; public class AddDimensionedImage { // Four constants that determine how - and indeed whether - the rows - // and columns an image may overlie should be expanded to accomodate that + // and columns an image may overlie should be expanded to accommodate that // image. // Passing EXPAND_ROW will result in the height of a row being increased - // to accomodate the image if it is not already larger. The image will + // to accommodate the image if it is not already larger. The image will // be layed across one or more columns. // Passing EXPAND_COLUMN will result in the width of the column being - // increased to accomodate the image if it is not already larger. The image + // increased to accommodate the image if it is not already larger. The image // will be layed across one or many rows. // Passing EXPAND_ROW_AND_COLUMN will result in the height of the row // bing increased along with the width of the column to accomdate the @@ -348,7 +348,7 @@ public class AddDimensionedImage { } // Call methods to calculate how the image and sheet should be - // manipulated to accomodate the image; columns and then rows. + // manipulated to accommodate the image; columns and then rows. colClientAnchorDetail = this.fitImageToColumns(sheet, colNumber, reqImageWidthMM, resizeBehaviour); rowClientAnchorDetail = this.fitImageToRows(sheet, rowNumber, @@ -440,7 +440,7 @@ public class AddDimensionedImage { colWidthMM = ConvertImageUnits.widthUnits2Millimetres( (short)sheet.getColumnWidth(colNumber)); - // Check that the column's width will accomodate the image at the + // Check that the column's width will accommodate the image at the // required dimension. If the width of the column is LESS than the // required width of the image, decide how the application should // respond - resize the column or overlay the image across one or more @@ -500,7 +500,7 @@ public class AddDimensionedImage { } /** - * Determines whether the sheets row should be re-sized to accomodate + * Determines whether the sheets row should be re-sized to accommodate * the image, adjusts the rows height if necessary and creates then * returns a ClientAnchorDetail object that facilitates construction of * a ClientAnchor that will fix the image on the sheet and establish @@ -542,7 +542,7 @@ public class AddDimensionedImage { // Get the row's height in millimetres rowHeightMM = row.getHeightInPoints() / ConvertImageUnits.POINTS_PER_MILLIMETRE; - // Check that the row's height will accomodate the image at the required + // Check that the row's height will accommodate the image at the required // dimensions. If the height of the row is LESS than the required height // of the image, decide how the application should respond - resize the // row or overlay the image across a series of rows. diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/NewLinesInCells.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/NewLinesInCells.java index 08684ca1c0..c3b4c1c0a0 100644 --- a/src/examples/src/org/apache/poi/xssf/usermodel/examples/NewLinesInCells.java +++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/NewLinesInCells.java @@ -44,8 +44,8 @@ public class NewLinesInCells { cs.setWrapText(true); cell.setCellStyle(cs); - //increase row height to accomodate two lines of text - row.setHeightInPoints((2 * sheet.getDefaultRowHeightInPoints())); + //increase row height to accommodate two lines of text + row.setHeightInPoints(2 * sheet.getDefaultRowHeightInPoints()); //adjust column width to fit the content sheet.autoSizeColumn(2); @@ -55,5 +55,4 @@ public class NewLinesInCells { } } } - } diff --git a/src/java/org/apache/poi/POIDocument.java b/src/java/org/apache/poi/POIDocument.java index 7c1d4779b5..69033a3f5f 100644 --- a/src/java/org/apache/poi/POIDocument.java +++ b/src/java/org/apache/poi/POIDocument.java @@ -33,6 +33,7 @@ import org.apache.poi.hpsf.DocumentSummaryInformation; import org.apache.poi.hpsf.PropertySet; import org.apache.poi.hpsf.PropertySetFactory; import org.apache.poi.hpsf.SummaryInformation; +import org.apache.poi.hpsf.WritingNotSupportedException; import org.apache.poi.poifs.crypt.EncryptionInfo; import org.apache.poi.poifs.crypt.Encryptor; import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor; @@ -176,6 +177,8 @@ public abstract class POIDocument implements Closeable { * * @param setName The property to read * @return The value of the given property or null if it wasn't found. + * + * @throws IOException If retrieving properties fails */ @SuppressWarnings("WeakerAccess") protected PropertySet getPropertySet(String setName) throws IOException { @@ -189,6 +192,8 @@ public abstract class POIDocument implements Closeable { * @param setName The property to read * @param encryptionInfo the encryption descriptor in case of cryptoAPI encryption * @return The value of the given property or null if it wasn't found. + * + * @throws IOException If retrieving properties fails */ @SuppressWarnings("WeakerAccess") protected PropertySet getPropertySet(String setName, EncryptionInfo encryptionInfo) throws IOException { @@ -305,7 +310,8 @@ public abstract class POIDocument implements Closeable { } /** - * Writes out a given ProperySet + * Writes out a given PropertySet + * * @param name the (POIFS Level) name of the property to write * @param set the PropertySet to write out * @param outFS the NPOIFSFileSystem to write the property into @@ -326,7 +332,7 @@ public abstract class POIDocument implements Closeable { outFS.createOrUpdateDocument(bIn, name); logger.log(POILogger.INFO, "Wrote property set " + name + " of size " + data.length); - } catch(org.apache.poi.hpsf.WritingNotSupportedException wnse) { + } catch(WritingNotSupportedException ignored) { logger.log( POILogger.ERROR, "Couldn't write property set with name " + name + " as not supported by HPSF yet"); } } @@ -468,6 +474,8 @@ public abstract class POIDocument implements Closeable { /** * @return the encryption info if the document is encrypted, otherwise {@code null} + * + * @throws IOException If retrieving the encryption information fails */ public EncryptionInfo getEncryptionInfo() throws IOException { return null; diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java b/src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java index 8faa788f58..b627e10af8 100644 --- a/src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java +++ b/src/java/org/apache/poi/common/usermodel/fonts/FontFamily.java @@ -68,13 +68,15 @@ public enum FontFamily { } return null; } - /** * Get FontFamily from combined native id + * + * @param pitchAndFamily The PitchFamily to decode. + * + * @return The resulting FontFamily */ public static FontFamily valueOfPitchFamily(byte pitchAndFamily) { return valueOf(pitchAndFamily >>> 4); } - -}
\ No newline at end of file +} diff --git a/src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java b/src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java index 78c6533944..5d1d48e63e 100644 --- a/src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java +++ b/src/java/org/apache/poi/common/usermodel/fonts/FontPitch.java @@ -59,6 +59,11 @@ public enum FontPitch { * Combine pitch and family to native id * * @see <a href="https://msdn.microsoft.com/en-us/library/dd145037.aspx">LOGFONT structure</a> + * + * @param pitch The pitch-value, cannot be null + * @param family The family-value, cannot be null + * + * @return The resulting combined byte-value with pitch and family encoded into one byte */ public static byte getNativeId(FontPitch pitch, FontFamily family) { return (byte)(pitch.getNativeId() | (family.getFlag() << 4)); @@ -66,9 +71,12 @@ public enum FontPitch { /** * Get FontPitch from native id + * + * @param pitchAndFamily The combined byte value for pitch and family + * + * @return The resulting FontPitch enumeration value */ public static FontPitch valueOfPitchFamily(byte pitchAndFamily) { return valueOf(pitchAndFamily & 0x3); } } - diff --git a/src/java/org/apache/poi/extractor/OLE2ExtractorFactory.java b/src/java/org/apache/poi/extractor/OLE2ExtractorFactory.java index 145d4d56ae..1f5eee039d 100644 --- a/src/java/org/apache/poi/extractor/OLE2ExtractorFactory.java +++ b/src/java/org/apache/poi/extractor/OLE2ExtractorFactory.java @@ -65,6 +65,8 @@ public final class OLE2ExtractorFactory { * Should this thread prefer event based over usermodel based extractors? * (usermodel extractors tend to be more accurate, but use more memory) * Default is false. + * + * @return true if event extractors should be preferred in the current thread, fals otherwise. */ public static boolean getThreadPrefersEventExtractors() { return threadPreferEventExtractors.get(); @@ -74,6 +76,8 @@ public final class OLE2ExtractorFactory { * Should all threads prefer event based over usermodel based extractors? * (usermodel extractors tend to be more accurate, but use more memory) * Default is to use the thread level setting, which defaults to false. + * + * @return true if event extractors should be preferred in all threads, fals otherwise. */ public static Boolean getAllThreadsPreferEventExtractors() { return allPreferEventExtractors; @@ -82,6 +86,8 @@ public final class OLE2ExtractorFactory { /** * Should this thread prefer event based over usermodel based extractors? * Will only be used if the All Threads setting is null. + * + * @param preferEventExtractors If this threads should prefer event based extractors. */ public static void setThreadPrefersEventExtractors(boolean preferEventExtractors) { threadPreferEventExtractors.set(preferEventExtractors); @@ -90,6 +96,8 @@ public final class OLE2ExtractorFactory { /** * Should all threads prefer event based over usermodel based extractors? * If set, will take preference over the Thread level setting. + * + * @param preferEventExtractors If all threads should prefer event based extractors. */ public static void setAllThreadsPreferEventExtractors(Boolean preferEventExtractors) { allPreferEventExtractors = preferEventExtractors; @@ -98,6 +106,8 @@ public final class OLE2ExtractorFactory { /** * Should this thread use event based extractors is available? * Checks the all-threads one first, then thread specific. + * + * @return If the current thread should use event based extractors. */ public static boolean getPreferEventExtractor() { if(allPreferEventExtractors != null) { @@ -155,6 +165,16 @@ public final class OLE2ExtractorFactory { * Create the Extractor, if possible. Generally needs the Scratchpad jar. * Note that this won't check for embedded OOXML resources either, use * {@link org.apache.poi.ooxml.extractor.ExtractorFactory} for that. + * + * @param poifsDir The {@link DirectoryNode} pointing to a document. + * + * @return The resulting {@link POITextExtractor}, an exception is thrown if + * no TextExtractor can be created for some reason. + * + * @throws IOException If converting the {@link DirectoryNode} into a HSSFWorkbook fails + * @throws OldFileFormatException If the {@link DirectoryNode} points to a format of + * an unsupported version of Excel. + * @throws IllegalArgumentException If creating the Extractor fails */ public static POITextExtractor createExtractor(DirectoryNode poifsDir) throws IOException { // Look for certain entries in the stream, to figure it @@ -193,11 +213,17 @@ public final class OLE2ExtractorFactory { * If there are no embedded documents, you'll get back an * empty array. Otherwise, you'll get one open * {@link POITextExtractor} for each embedded file. + * + * @param ext The extractor to look at for embedded documents + * + * @return An array of resulting extractors. Empty if no embedded documents are found. + * + * @throws IOException If converting the {@link DirectoryNode} into a HSSFWorkbook fails + * @throws OldFileFormatException If the {@link DirectoryNode} points to a format of + * an unsupported version of Excel. + * @throws IllegalArgumentException If creating the Extractor fails */ - @SuppressWarnings("unused") - public static POITextExtractor[] getEmbededDocsTextExtractors(POIOLE2TextExtractor ext) - throws IOException - { + public static POITextExtractor[] getEmbededDocsTextExtractors(POIOLE2TextExtractor ext) throws IOException { // All the embedded directories we spotted List<Entry> dirs = new ArrayList<>(); // For anything else not directly held in as a POIFS directory @@ -237,13 +263,12 @@ public final class OLE2ExtractorFactory { ArrayList<POITextExtractor> e = new ArrayList<>(); for (Entry dir : dirs) { - e.add(createExtractor( - (DirectoryNode) dir + e.add(createExtractor((DirectoryNode) dir )); } - for (InputStream nonPOIF : nonPOIFS) { + for (InputStream stream : nonPOIFS) { try { - e.add(createExtractor(nonPOIF)); + e.add(createExtractor(stream)); } catch (Exception xe) { // Ignore, invalid format LOGGER.log(POILogger.WARN, xe); diff --git a/src/java/org/apache/poi/hpsf/Property.java b/src/java/org/apache/poi/hpsf/Property.java index 8878497dc3..56f9ac2175 100644 --- a/src/java/org/apache/poi/hpsf/Property.java +++ b/src/java/org/apache/poi/hpsf/Property.java @@ -248,12 +248,14 @@ public class Property { /** * Returns the property's size in bytes. This is always a multiple of 4. * + * @param property The integer property to check + * * @return the property's size in bytes * * @exception WritingNotSupportedException if HPSF does not yet support the * property's variant type. */ - protected int getSize(int codepage) throws WritingNotSupportedException + protected int getSize(int property) throws WritingNotSupportedException { int length = Variant.getVariantLength(type); if (length >= 0 || type == Variant.VT_EMPTY) { @@ -269,16 +271,16 @@ public class Property { if (type == Variant.VT_LPSTR || type == Variant.VT_LPWSTR) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { - length = write(bos, codepage) - 2*LittleEndianConsts.INT_SIZE; + length = write(bos, property) - 2*LittleEndianConsts.INT_SIZE; /* Pad to multiples of 4. */ length += (4 - (length & 0x3)) & 0x3; return length; } catch (IOException e) { - throw new WritingNotSupportedException(type, value); + throw new WritingNotSupportedException(type, this.value); } } - throw new WritingNotSupportedException(type, value); + throw new WritingNotSupportedException(type, this.value); } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbookFactory.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbookFactory.java index e1413aad3b..96196707f6 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbookFactory.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbookFactory.java @@ -32,6 +32,15 @@ import org.apache.poi.util.Internal; @Internal public class HSSFWorkbookFactory extends WorkbookFactory { /** + * Create a new empty Workbook + * + * @return The created workbook + */ + public static HSSFWorkbook createWorkbook() { + return new HSSFWorkbook(); + } + + /** * Creates a HSSFWorkbook from the given NPOIFSFileSystem<p> * Note that in order to properly release resources the * Workbook should be closed after use. diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java index c3a1c89e78..8254b86d8a 100644 --- a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java +++ b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java @@ -20,9 +20,19 @@ package org.apache.poi.poifs.macros; import static org.apache.poi.util.StringUtil.endsWithIgnoreCase; import static org.apache.poi.util.StringUtil.startsWithIgnoreCase; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -39,6 +49,8 @@ import org.apache.poi.util.CodePageUtil; import org.apache.poi.util.HexDump; import org.apache.poi.util.IOUtils; import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; import org.apache.poi.util.RLEDecompressingInputStream; import org.apache.poi.util.StringUtil; @@ -56,6 +68,10 @@ import org.apache.poi.util.StringUtil; * @since 3.15-beta2 */ public class VBAMacroReader implements Closeable { + private static final POILogger LOGGER = POILogFactory.getLogger(VBAMacroReader.class); + + //arbitrary limit on size of strings to read, etc. + private static final int MAX_STRING_LENGTH = 20000; protected static final String VBA_PROJECT_OOXML = "vbaProject.bin"; protected static final String VBA_PROJECT_POIFS = "VBA"; @@ -111,8 +127,13 @@ public class VBAMacroReader implements Closeable { public Map<String, Module> readMacroModules() throws IOException { final ModuleMap modules = new ModuleMap(); + //ascii -> unicode mapping for module names + //preserve insertion order + final Map<String, String> moduleNameMap = new LinkedHashMap<>(); + findMacros(fs.getRoot(), modules); - findProjectProperties(fs.getRoot(), modules); + findModuleNameMap(fs.getRoot(), moduleNameMap, modules); + findProjectProperties(fs.getRoot(), moduleNameMap, modules); Map<String, Module> moduleSources = new HashMap<>(); for (Map.Entry<String, ModuleImpl> entry : modules.entrySet()) { @@ -327,19 +348,30 @@ public class VBAMacroReader implements Closeable { } } - protected void findProjectProperties(DirectoryNode node, ModuleMap modules) throws IOException { + protected void findProjectProperties(DirectoryNode node, Map<String, String> moduleNameMap, ModuleMap modules) throws IOException { for (Entry entry : node) { if ("project".equalsIgnoreCase(entry.getName())) { DocumentNode document = (DocumentNode)entry; - DocumentInputStream dis = new DocumentInputStream(document); - readProjectProperties(dis, modules); - } else { - for (Entry child : node) { - if (child instanceof DirectoryNode) { - findProjectProperties((DirectoryNode)child, modules); - } + try(DocumentInputStream dis = new DocumentInputStream(document)) { + readProjectProperties(dis, moduleNameMap, modules); + return; } + } else if (entry instanceof DirectoryNode) { + findProjectProperties((DirectoryNode)entry, moduleNameMap, modules); + } + } + } + protected void findModuleNameMap(DirectoryNode node, Map<String, String> moduleNameMap, ModuleMap modules) throws IOException { + for (Entry entry : node) { + if ("projectwm".equalsIgnoreCase(entry.getName())) { + DocumentNode document = (DocumentNode)entry; + try(DocumentInputStream dis = new DocumentInputStream(document)) { + readNameMapRecords(dis, moduleNameMap, modules.charset); + return; + } + } else if (entry.isDirectoryEntry()) { + findModuleNameMap((DirectoryNode)entry, moduleNameMap, modules); } } } @@ -426,10 +458,18 @@ public class VBAMacroReader implements Closeable { private static class ASCIIUnicodeStringPair { private final String ascii; private final String unicode; + private final int pushbackRecordId; + + ASCIIUnicodeStringPair(String ascii, int pushbackRecordId) { + this.ascii = ascii; + this.unicode = ""; + this.pushbackRecordId = pushbackRecordId; + } ASCIIUnicodeStringPair(String ascii, String unicode) { this.ascii = ascii; this.unicode = unicode; + pushbackRecordId = -1; } private String getAscii() { @@ -439,6 +479,10 @@ public class VBAMacroReader implements Closeable { private String getUnicode() { return unicode; } + + private int getPushbackRecordId() { + return pushbackRecordId; + } } private void processDirStream(Entry dir, ModuleMap modules) throws IOException { @@ -447,7 +491,7 @@ public class VBAMacroReader implements Closeable { try (DocumentInputStream dis = new DocumentInputStream(dirDocumentNode)) { String streamName = null; int recordId = 0; - boolean inReferenceTwiddled = false; + try (RLEDecompressingInputStream in = new RLEDecompressingInputStream(dis)) { while (true) { recordId = in.readShort(); @@ -485,7 +529,27 @@ public class VBAMacroReader implements Closeable { if (dirState.equals(DIR_STATE.INFORMATION_RECORD)) { dirState = DIR_STATE.REFERENCES_RECORD; } - readStringPair(in, modules.charset, REFERENCE_NAME_RESERVED); + ASCIIUnicodeStringPair stringPair = readStringPair(in, + modules.charset, REFERENCE_NAME_RESERVED, false); + if (stringPair.getPushbackRecordId() == -1) { + break; + } + //Special handling for when there's only an ascii string and a REFERENCED_REGISTERED + //record that follows. + //See https://github.com/decalage2/oletools/blob/master/oletools/olevba.py#L1516 + //and https://github.com/decalage2/oletools/pull/135 from (@c1fe) + if (stringPair.getPushbackRecordId() != RecordType.REFERENCE_REGISTERED.id) { + throw new IllegalArgumentException("Unexpected reserved character. "+ + "Expected "+Integer.toHexString(REFERENCE_NAME_RESERVED) + + " or "+Integer.toHexString(RecordType.REFERENCE_REGISTERED.id)+ + " not: "+Integer.toHexString(stringPair.getPushbackRecordId())); + } + //fall through! + case REFERENCE_REGISTERED: + //REFERENCE_REGISTERED must come immediately after + //REFERENCE_NAME to allow for fall through in special case of bug 62625 + int recLength = in.readInt(); + trySkip(in, recLength); break; case MODULE_DOC_STRING: int modDocStringLength = in.readInt(); @@ -546,19 +610,105 @@ public class VBAMacroReader implements Closeable { } } - private ASCIIUnicodeStringPair readStringPair(RLEDecompressingInputStream in, Charset charset, int reservedByte) throws IOException { + + + private ASCIIUnicodeStringPair readStringPair(RLEDecompressingInputStream in, + Charset charset, int reservedByte) throws IOException { + return readStringPair(in, charset, reservedByte, true); + } + + private ASCIIUnicodeStringPair readStringPair(RLEDecompressingInputStream in, + Charset charset, int reservedByte, + boolean throwOnUnexpectedReservedByte) throws IOException { int nameLength = in.readInt(); String ascii = readString(in, nameLength, charset); int reserved = in.readShort(); + if (reserved != reservedByte) { - throw new IOException("Expected "+Integer.toHexString(reservedByte)+ "after name before Unicode name, but found: " + - Integer.toHexString(reserved)); + if (throwOnUnexpectedReservedByte) { + throw new IOException("Expected " + Integer.toHexString(reservedByte) + + "after name before Unicode name, but found: " + + Integer.toHexString(reserved)); + } else { + return new ASCIIUnicodeStringPair(ascii, reserved); + } } int unicodeNameRecordLength = in.readInt(); String unicode = readUnicodeString(in, unicodeNameRecordLength); return new ASCIIUnicodeStringPair(ascii, unicode); } + protected void readNameMapRecords(InputStream is, + Map<String, String> moduleNames, Charset charset) throws IOException { + //see 2.3.3 PROJECTwm Stream: Module Name Information + //multibytecharstring + String mbcs = null; + String unicode = null; + //arbitrary sanity threshold + final int maxNameRecords = 10000; + int records = 0; + while (++records < maxNameRecords) { + try { + int b = IOUtils.readByte(is); + //check for two 0x00 that mark end of record + if (b == 0) { + b = IOUtils.readByte(is); + if (b == 0) { + return; + } + } + mbcs = readMBCS(b, is, charset, MAX_STRING_LENGTH); + } catch (EOFException e) { + return; + } + + try { + unicode = readUnicode(is, MAX_STRING_LENGTH); + } catch (EOFException e) { + return; + } + if (mbcs.trim().length() > 0 && unicode.trim().length() > 0) { + moduleNames.put(mbcs, unicode); + } + + } + if (records >= maxNameRecords) { + LOGGER.log(POILogger.WARN, "Hit max name records to read ("+maxNameRecords+"). Stopped early."); + } + } + + private static String readUnicode(InputStream is, int maxLength) throws IOException { + //reads null-terminated unicode string + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int b0 = IOUtils.readByte(is); + int b1 = IOUtils.readByte(is); + + int read = 2; + while ((b0 + b1) != 0 && read < maxLength) { + + bos.write(b0); + bos.write(b1); + b0 = IOUtils.readByte(is); + b1 = IOUtils.readByte(is); + read += 2; + } + if (read >= maxLength) { + LOGGER.log(POILogger.WARN, "stopped reading unicode name after "+read+" bytes"); + } + return new String (bos.toByteArray(), StandardCharsets.UTF_16LE); + } + + private static String readMBCS(int firstByte, InputStream is, Charset charset, int maxLength) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int len = 0; + int b = firstByte; + while (b > 0 && len < maxLength) { + ++len; + bos.write(b); + b = IOUtils.readByte(is); + } + return new String(bos.toByteArray(), charset); + } /** * Read <tt>length</tt> bytes of MBCS (multi-byte character set) characters from the stream @@ -570,7 +720,7 @@ public class VBAMacroReader implements Closeable { * @throws IOException If reading from the stream fails */ private static String readString(InputStream stream, int length, Charset charset) throws IOException { - byte[] buffer = IOUtils.safelyAllocate(length, 20000); + byte[] buffer = IOUtils.safelyAllocate(length, MAX_STRING_LENGTH); int bytesRead = IOUtils.readFully(stream, buffer); if (bytesRead != length) { throw new IOException("Tried to read: "+length + @@ -579,7 +729,8 @@ public class VBAMacroReader implements Closeable { return new String(buffer, 0, length, charset); } - protected void readProjectProperties(DocumentInputStream dis, ModuleMap modules) throws IOException { + protected void readProjectProperties(DocumentInputStream dis, + Map<String, String> moduleNameMap, ModuleMap modules) throws IOException { InputStreamReader reader = new InputStreamReader(dis, modules.charset); StringBuilder builder = new StringBuilder(); char[] buffer = new char[512]; @@ -588,6 +739,9 @@ public class VBAMacroReader implements Closeable { builder.append(buffer, 0, read); } String properties = builder.toString(); + //the module name map names should be in exactly the same order + //as the module names here. See 2.3.3 PROJECTwm Stream. + //At some point, we might want to enforce that. for (String line : properties.split("\r\n|\n\r")) { if (!line.startsWith("[")) { String[] tokens = line.split("="); @@ -595,28 +749,46 @@ public class VBAMacroReader implements Closeable { && tokens[1].startsWith("\"") && tokens[1].endsWith("\"")) { // Remove any double quotes tokens[1] = tokens[1].substring(1, tokens[1].length() - 1); - } - if ("Document".equals(tokens[0])) { + if ("Document".equals(tokens[0]) && tokens.length > 1) { String mn = tokens[1].substring(0, tokens[1].indexOf("/&H")); - ModuleImpl module = modules.get(mn); - module.moduleType = ModuleType.Document; - } else if ("Module".equals(tokens[0])) { - ModuleImpl module = modules.get(tokens[1]); - module.moduleType = ModuleType.Module; - } else if ("Class".equals(tokens[0])) { - ModuleImpl module = modules.get(tokens[1]); - module.moduleType = ModuleType.Class; + ModuleImpl module = getModule(mn, moduleNameMap, modules); + if (module != null) { + module.moduleType = ModuleType.Document; + } else { + LOGGER.log(POILogger.WARN, "couldn't find module with name: "+mn); + } + } else if ("Module".equals(tokens[0]) && tokens.length > 1) { + ModuleImpl module = getModule(tokens[1], moduleNameMap, modules); + if (module != null) { + module.moduleType = ModuleType.Module; + } else { + LOGGER.log(POILogger.WARN, "couldn't find module with name: "+tokens[1]); + } + } else if ("Class".equals(tokens[0]) && tokens.length > 1) { + ModuleImpl module = getModule(tokens[1], moduleNameMap, modules); + if (module != null) { + module.moduleType = ModuleType.Class; + } else { + LOGGER.log(POILogger.WARN, "couldn't find module with name: "+tokens[1]); + } } } } } + //can return null! + private ModuleImpl getModule(String moduleName, Map<String, String> moduleNameMap, ModuleMap moduleMap) { + if (moduleNameMap.containsKey(moduleName)) { + return moduleMap.get(moduleNameMap.get(moduleName)); + } + return moduleMap.get(moduleName); + } private String readUnicodeString(RLEDecompressingInputStream in, int unicodeNameRecordLength) throws IOException { - byte[] buffer = IOUtils.safelyAllocate(unicodeNameRecordLength, 20000); + byte[] buffer = IOUtils.safelyAllocate(unicodeNameRecordLength, MAX_STRING_LENGTH); int bytesRead = IOUtils.readFully(in, buffer); if (bytesRead != unicodeNameRecordLength) { - + throw new EOFException(); } return new String(buffer, StringUtil.UTF16LE); } diff --git a/src/java/org/apache/poi/ss/format/CellDateFormatter.java b/src/java/org/apache/poi/ss/format/CellDateFormatter.java index 42b71bddb1..b544d9ca16 100644 --- a/src/java/org/apache/poi/ss/format/CellDateFormatter.java +++ b/src/java/org/apache/poi/ss/format/CellDateFormatter.java @@ -50,6 +50,7 @@ public class CellDateFormatter extends CellFormatter { private int hStart = -1; private int hLen; + @Override public String handlePart(Matcher m, String part, CellFormatType type, StringBuffer desc) { diff --git a/src/java/org/apache/poi/ss/format/CellTextFormatter.java b/src/java/org/apache/poi/ss/format/CellTextFormatter.java index 5712bb24ab..08736fa584 100644 --- a/src/java/org/apache/poi/ss/format/CellTextFormatter.java +++ b/src/java/org/apache/poi/ss/format/CellTextFormatter.java @@ -16,10 +16,7 @@ ==================================================================== */ package org.apache.poi.ss.format; -import org.apache.poi.ss.format.CellFormatPart.PartHandler; - import java.util.Locale; -import java.util.regex.Matcher; /** * This class implements printing out text. @@ -38,15 +35,12 @@ public class CellTextFormatter extends CellFormatter { final int[] numPlaces = new int[1]; desc = CellFormatPart.parseFormat(format, CellFormatType.TEXT, - new PartHandler() { - public String handlePart(Matcher m, String part, - CellFormatType type, StringBuffer desc) { - if (part.equals("@")) { - numPlaces[0]++; - return "\u0000"; - } - return null; + (m, part, type, desc) -> { + if (part.equals("@")) { + numPlaces[0]++; + return "\u0000"; } + return null; }).toString(); // Remember the "@" positions in last-to-first order (to make insertion easier) @@ -66,8 +60,8 @@ public class CellTextFormatter extends CellFormatter { text = text.toUpperCase(Locale.ROOT); } toAppendTo.append(desc); - for (int i = 0; i < textPos.length; i++) { - int pos = start + textPos[i]; + for (int textPo : textPos) { + int pos = start + textPo; toAppendTo.replace(pos, pos + 1, text); } } diff --git a/src/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java b/src/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java index 51a99d5950..ee0615057d 100644 --- a/src/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java +++ b/src/java/org/apache/poi/ss/formula/BaseFormulaEvaluator.java @@ -65,6 +65,14 @@ public abstract class BaseFormulaEvaluator implements FormulaEvaluator, Workbook } /** + * internal use + * @return evaluation workbook + */ + protected EvaluationWorkbook getEvaluationWorkbook() { + return _bookEvaluator.getWorkbook(); + } + + /** * Should be called whenever there are major changes (e.g. moving sheets) to input cells * in the evaluated workbook. If performance is not critical, a single call to this method * may be used instead of many specific calls to the notify~ methods. @@ -208,14 +216,19 @@ public abstract class BaseFormulaEvaluator implements FormulaEvaluator, Workbook return evaluateFormulaCell(cell); } - protected static void setCellType(Cell cell, CellValue cv) { + /** + * set the cell type + * @param cell + * @param cv + */ + protected void setCellType(Cell cell, CellValue cv) { CellType cellType = cv.getCellType(); switch (cellType) { case BOOLEAN: case ERROR: case NUMERIC: case STRING: - cell.setCellType(cellType); + setCellType(cell, cellType); return; case BLANK: // never happens - blanks eventually get translated to zero @@ -227,6 +240,15 @@ public abstract class BaseFormulaEvaluator implements FormulaEvaluator, Workbook throw new IllegalStateException("Unexpected cell value type (" + cellType + ")"); } } + + /** + * Override if a different variation is needed, e.g. passing the evaluator to the cell method + * @param cell + * @param cellType + */ + protected void setCellType(Cell cell, CellType cellType) { + cell.setCellType(cellType); + } protected abstract RichTextString createRichTextString(String str); diff --git a/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java b/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java index 0c55327a4a..961a9cd81c 100644 --- a/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java @@ -115,7 +115,7 @@ public final class FunctionEval { // 47: DVAR retval[48] = TextFunction.TEXT; // 49: LINEST - // 50: TREND + retval[50] = new Trend(); // 51: LOGEST // 52: GROWTH @@ -243,7 +243,8 @@ public final class FunctionEval { // 247: DB // 252: FEQUENCY - + retval[252] = Frequency.instance; + retval[FunctionID.EXTERNAL_FUNC] = null; // ExternalFunction is a FreeRefFunction, nominally 255 retval[261] = new Errortype(); diff --git a/src/java/org/apache/poi/ss/formula/functions/Frequency.java b/src/java/org/apache/poi/ss/formula/functions/Frequency.java new file mode 100644 index 0000000000..9df832366d --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/functions/Frequency.java @@ -0,0 +1,81 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.ss.formula.functions; + +import org.apache.poi.ss.formula.CacheAreaEval; +import org.apache.poi.ss.formula.eval.EvaluationException; +import org.apache.poi.ss.formula.eval.NumberEval; +import org.apache.poi.ss.formula.eval.ValueEval; + +import java.util.Arrays; + +/** + * Implementation of Excel 'Analysis ToolPak' function FREQUENCY()<br> + * Returns a frequency distribution as a vertical array<p> + * <p> + * <b>Syntax</b><br> + * <b>FREQUENCY</b>(<b>data_array</b>, <b>bins_array</b>)<p> + * <p> + * <b>data_array</b> Required. An array of or reference to a set of values for which you want to count frequencies. + * If data_array contains no values, FREQUENCY returns an array of zeros.<br> + * <b>bins_array</b> Required. An array of or reference to intervals into which you want to group the values in data_array. + * If bins_array contains no values, FREQUENCY returns the number of elements in data_array.<br> + * + * @author Yegor Kozlov + */ +public class Frequency extends Fixed2ArgFunction { + public static final Function instance = new Frequency(); + + private Frequency() { + // enforce singleton + } + + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) { + MatrixFunction.MutableValueCollector collector = new MatrixFunction.MutableValueCollector(false, false); + + double[] values; + double[] bins; + try { + values = collector.collectValues(arg0); + bins = collector.collectValues(arg1); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + + // can bins be not sorted? + //bins = Arrays.stream(bins).sorted().distinct().toArray(); + + int[] histogram = histogram(values, bins); + NumberEval[] result = Arrays.stream(histogram).boxed().map(NumberEval::new).toArray(NumberEval[]::new); + return new CacheAreaEval(srcRowIndex, srcColumnIndex, + srcRowIndex + result.length - 1, srcColumnIndex, result); + } + + static int findBin(double value, double[] bins) { + int idx = Arrays.binarySearch(bins, value); + return idx >= 0 ? idx + 1 : -idx; + } + + static int[] histogram(double[] values, double[] bins) { + int[] histogram = new int[bins.length + 1]; + for (double val : values) { + histogram[findBin(val, bins) - 1]++; + } + return histogram; + } +} diff --git a/src/java/org/apache/poi/ss/formula/functions/Trend.java b/src/java/org/apache/poi/ss/formula/functions/Trend.java new file mode 100644 index 0000000000..155c1a57a5 --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/functions/Trend.java @@ -0,0 +1,377 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* + * Notes: + * Duplicate x values don't work most of the time because of the way the + * math library handles multiple regression. + * The math library currently fails when the number of x variables is >= + * the sample size (see https://github.com/Hipparchus-Math/hipparchus/issues/13). + */ + +package org.apache.poi.ss.formula.functions; + +import org.apache.poi.ss.formula.CacheAreaEval; +import org.apache.poi.ss.formula.eval.AreaEval; +import org.apache.poi.ss.formula.eval.BoolEval; +import org.apache.poi.ss.formula.eval.ErrorEval; +import org.apache.poi.ss.formula.eval.EvaluationException; +import org.apache.poi.ss.formula.eval.MissingArgEval; +import org.apache.poi.ss.formula.eval.NotImplementedException; +import org.apache.poi.ss.formula.eval.NumberEval; +import org.apache.poi.ss.formula.eval.NumericValueEval; +import org.apache.poi.ss.formula.eval.RefEval; +import org.apache.poi.ss.formula.eval.ValueEval; +import org.apache.commons.math3.linear.SingularMatrixException; +import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression; + +import java.util.Arrays; + + +/** + * Implementation for the Excel function TREND<p> + * + * Syntax:<br> + * TREND(known_y's, known_x's, new_x's, constant) + * <table border="0" cellpadding="1" cellspacing="0" summary="Parameter descriptions"> + * <tr><th>known_y's, known_x's, new_x's</th><td>typically area references, possibly cell references or scalar values</td></tr> + * <tr><th>constant</th><td><b>TRUE</b> or <b>FALSE</b>: + * determines whether the regression line should include an intercept term</td></tr> + * </table><br> + * If <b>known_x's</b> is not given, it is assumed to be the default array {1, 2, 3, ...} + * of the same size as <b>known_y's</b>.<br> + * If <b>new_x's</b> is not given, it is assumed to be the same as <b>known_x's</b><br> + * If <b>constant</b> is omitted, it is assumed to be <b>TRUE</b> + * </p> + */ + +public final class Trend implements Function { + MatrixFunction.MutableValueCollector collector = new MatrixFunction.MutableValueCollector(false, false); + private static final class TrendResults { + public double[] vals; + public int resultWidth; + public int resultHeight; + + public TrendResults(double[] vals, int resultWidth, int resultHeight) { + this.vals = vals; + this.resultWidth = resultWidth; + this.resultHeight = resultHeight; + } + } + + public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) { + if (args.length < 1 || args.length > 4) { + return ErrorEval.VALUE_INVALID; + } + try { + TrendResults tr = getNewY(args); + ValueEval[] vals = new ValueEval[tr.vals.length]; + for (int i = 0; i < tr.vals.length; i++) { + vals[i] = new NumberEval(tr.vals[i]); + } + if (tr.vals.length == 1) { + return vals[0]; + } + return new CacheAreaEval(srcRowIndex, srcColumnIndex, srcRowIndex + tr.resultHeight - 1, srcColumnIndex + tr.resultWidth - 1, vals); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + } + + private static double[][] evalToArray(ValueEval arg) throws EvaluationException { + double[][] ar; + ValueEval eval; + if (arg instanceof MissingArgEval) { + return new double[0][0]; + } + if (arg instanceof RefEval) { + RefEval re = (RefEval) arg; + if (re.getNumberOfSheets() > 1) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + eval = re.getInnerValueEval(re.getFirstSheetIndex()); + } else { + eval = arg; + } + if (eval == null) { + throw new RuntimeException("Parameter may not be null."); + } + + if (eval instanceof AreaEval) { + AreaEval ae = (AreaEval) eval; + int w = ae.getWidth(); + int h = ae.getHeight(); + ar = new double[h][w]; + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + ValueEval ve = ae.getRelativeValue(i, j); + if (!(ve instanceof NumericValueEval)) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + ar[i][j] = ((NumericValueEval)ve).getNumberValue(); + } + } + } else if (eval instanceof NumericValueEval) { + ar = new double[1][1]; + ar[0][0] = ((NumericValueEval)eval).getNumberValue(); + } else { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + + return ar; + } + + private static double[][] getDefaultArrayOneD(int w) { + double[][] array = new double[w][1]; + for (int i = 0; i < w; i++) { + array[i][0] = i + 1; + } + return array; + } + + private static double[] flattenArray(double[][] twoD) { + if (twoD.length < 1) { + return new double[0]; + } + double[] oneD = new double[twoD.length * twoD[0].length]; + for (int i = 0; i < twoD.length; i++) { + for (int j = 0; j < twoD[0].length; j++) { + oneD[i * twoD[0].length + j] = twoD[i][j]; + } + } + return oneD; + } + + private static double[][] flattenArrayToRow(double[][] twoD) { + if (twoD.length < 1) { + return new double[0][0]; + } + double[][] oneD = new double[twoD.length * twoD[0].length][1]; + for (int i = 0; i < twoD.length; i++) { + for (int j = 0; j < twoD[0].length; j++) { + oneD[i * twoD[0].length + j][0] = twoD[i][j]; + } + } + return oneD; + } + + private static double[][] switchRowsColumns(double[][] array) { + double[][] newArray = new double[array[0].length][array.length]; + for (int i = 0; i < array.length; i++) { + for (int j = 0; j < array[0].length; j++) { + newArray[j][i] = array[i][j]; + } + } + return newArray; + } + + /** + * Check if all columns in a matrix contain the same values. + * Return true if the number of distinct values in each column is 1. + * + * @param matrix column-oriented matrix. A Row matrix should be transposed to column . + * @return true if all columns contain the same value + */ + private static boolean isAllColumnsSame(double[][] matrix){ + if(matrix.length == 0) return false; + + boolean[] cols = new boolean[matrix[0].length]; + for (int j = 0; j < matrix[0].length; j++) { + double prev = Double.NaN; + for (int i = 0; i < matrix.length; i++) { + double v = matrix[i][j]; + if(i > 0 && v != prev) { + cols[j] = true; + break; + } + prev = v; + } + } + boolean allEquals = true; + for (boolean x : cols) { + if(x) { + allEquals = false; + break; + } + }; + return allEquals; + + } + + private static TrendResults getNewY(ValueEval[] args) throws EvaluationException { + double[][] xOrig; + double[][] x; + double[][] yOrig; + double[] y; + double[][] newXOrig; + double[][] newX; + double[][] resultSize; + boolean passThroughOrigin = false; + switch (args.length) { + case 1: + yOrig = evalToArray(args[0]); + xOrig = new double[0][0]; + newXOrig = new double[0][0]; + break; + case 2: + yOrig = evalToArray(args[0]); + xOrig = evalToArray(args[1]); + newXOrig = new double[0][0]; + break; + case 3: + yOrig = evalToArray(args[0]); + xOrig = evalToArray(args[1]); + newXOrig = evalToArray(args[2]); + break; + case 4: + yOrig = evalToArray(args[0]); + xOrig = evalToArray(args[1]); + newXOrig = evalToArray(args[2]); + if (!(args[3] instanceof BoolEval)) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + // The argument in Excel is false when it *should* pass through the origin. + passThroughOrigin = !((BoolEval)args[3]).getBooleanValue(); + break; + default: + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + + if (yOrig.length < 1) { + throw new EvaluationException(ErrorEval.VALUE_INVALID); + } + y = flattenArray(yOrig); + newX = newXOrig; + + if (newXOrig.length > 0) { + resultSize = newXOrig; + } else { + resultSize = new double[1][1]; + } + + if (y.length == 1) { + /* See comment at top of file + if (xOrig.length > 0 && !(xOrig.length == 1 || xOrig[0].length == 1)) { + throw new EvaluationException(ErrorEval.REF_INVALID); + } else if (xOrig.length < 1) { + x = new double[1][1]; + x[0][0] = 1; + } else { + x = new double[1][]; + x[0] = flattenArray(xOrig); + if (newXOrig.length < 1) { + resultSize = xOrig; + } + }*/ + throw new NotImplementedException("Sample size too small"); + } else if (yOrig.length == 1 || yOrig[0].length == 1) { + if (xOrig.length < 1) { + x = getDefaultArrayOneD(y.length); + if (newXOrig.length < 1) { + resultSize = yOrig; + } + } else { + x = xOrig; + if (xOrig[0].length > 1 && yOrig.length == 1) { + x = switchRowsColumns(x); + } + if (newXOrig.length < 1) { + resultSize = xOrig; + } + } + if (newXOrig.length > 0 && (x.length == 1 || x[0].length == 1)) { + newX = flattenArrayToRow(newXOrig); + } + } else { + if (xOrig.length < 1) { + x = getDefaultArrayOneD(y.length); + if (newXOrig.length < 1) { + resultSize = yOrig; + } + } else { + x = flattenArrayToRow(xOrig); + if (newXOrig.length < 1) { + resultSize = xOrig; + } + } + if (newXOrig.length > 0) { + newX = flattenArrayToRow(newXOrig); + } + if (y.length != x.length || yOrig.length != xOrig.length) { + throw new EvaluationException(ErrorEval.REF_INVALID); + } + } + + if (newXOrig.length < 1) { + newX = x; + } else if (newXOrig.length == 1 && newXOrig[0].length > 1 && xOrig.length > 1 && xOrig[0].length == 1) { + newX = switchRowsColumns(newXOrig); + } + + if (newX[0].length != x[0].length) { + throw new EvaluationException(ErrorEval.REF_INVALID); + } + + if (x[0].length >= x.length) { + /* See comment at top of file */ + throw new NotImplementedException("Sample size too small"); + } + + int resultHeight = resultSize.length; + int resultWidth = resultSize[0].length; + + if(isAllColumnsSame(x)){ + double[] result = new double[newX.length]; + double avg = Arrays.stream(y).average().orElse(0); + for(int i = 0; i < result.length; i++) result[i] = avg; + return new TrendResults(result, resultWidth, resultHeight); + } + + OLSMultipleLinearRegression reg = new OLSMultipleLinearRegression(); + if (passThroughOrigin) { + reg.setNoIntercept(true); + } + + try { + reg.newSampleData(y, x); + } catch (IllegalArgumentException e) { + throw new EvaluationException(ErrorEval.REF_INVALID); + } + double[] par; + try { + par = reg.estimateRegressionParameters(); + } catch (SingularMatrixException e) { + throw new NotImplementedException("Singular matrix in input"); + } + + double[] result = new double[newX.length]; + for (int i = 0; i < newX.length; i++) { + result[i] = 0; + if (passThroughOrigin) { + for (int j = 0; j < par.length; j++) { + result[i] += par[j] * newX[i][j]; + } + } else { + result[i] = par[0]; + for (int j = 1; j < par.length; j++) { + result[i] += par[j] * newX[i][j - 1]; + } + } + } + return new TrendResults(result, resultWidth, resultHeight); + } +} diff --git a/src/java/org/apache/poi/ss/formula/ptg/RefPtgBase.java b/src/java/org/apache/poi/ss/formula/ptg/RefPtgBase.java index 6d63ed5a46..31a3a56be7 100644 --- a/src/java/org/apache/poi/ss/formula/ptg/RefPtgBase.java +++ b/src/java/org/apache/poi/ss/formula/ptg/RefPtgBase.java @@ -43,7 +43,7 @@ public abstract class RefPtgBase extends OperandPtg { /** * YK: subclasses of RefPtgBase are used by the FormulaParser and FormulaEvaluator accross HSSF and XSSF. - * The bit mask should accomodate the maximum number of avaiable columns, i.e. 0x3FFF. + * The bit mask should accommodate the maximum number of avaiable columns, i.e. 0x3FFF. * * @see org.apache.poi.ss.SpreadsheetVersion */ diff --git a/src/java/org/apache/poi/ss/usermodel/Sheet.java b/src/java/org/apache/poi/ss/usermodel/Sheet.java index 7e3eb3c574..d9dd4ec132 100644 --- a/src/java/org/apache/poi/ss/usermodel/Sheet.java +++ b/src/java/org/apache/poi/ss/usermodel/Sheet.java @@ -1006,7 +1006,10 @@ public interface Sheet extends Iterable<Row> { /** * Sets array formula to specified region for result. - * + * <p> + * Note if there are shared formulas this will invalidate any + * {@link FormulaEvaluator} instances based on this workbook + *</p> * @param formula text representation of the formula * @param range Region of array formula for result. * @return the {@link CellRange} of cells affected by this change diff --git a/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java b/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java index 50833aa6c2..832795a07f 100644 --- a/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java +++ b/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java @@ -44,6 +44,24 @@ import org.apache.poi.util.Removal; */ public class WorkbookFactory { /** + * Create a new empty Workbook, either XSSF or HSSF depending + * on the parameter + * + * @param xssf If an XSSFWorkbook or a HSSFWorkbook should be created + * + * @return The created workbook + * + * @throws IOException if an error occurs while reading the data + */ + public static Workbook create(boolean xssf) throws IOException { + if(xssf) { + return createXSSFWorkbook(); + } else { + return createHSSFWorkbook(); + } + } + + /** * Creates a HSSFWorkbook from the given NPOIFSFileSystem<p> * * Note that in order to properly release resources the @@ -296,6 +314,11 @@ public class WorkbookFactory { return createWorkbook("org.apache.poi.xssf.usermodel.XSSFWorkbookFactory", args); } + /** + * Does the actual call to HSSF or XSSF to do the creation. + * Uses reflection, so that this class can be in the Core non-OOXML + * POI jar without errors / broken references to the OOXML / XSSF code. + */ private static Workbook createWorkbook(String factoryClass, Object args[]) throws IOException, EncryptedDocumentException { try { Class<?> clazz = WorkbookFactory.class.getClassLoader().loadClass(factoryClass); @@ -307,6 +330,8 @@ public class WorkbookFactory { c = boolean.class; } else if (InputStream.class.isAssignableFrom(c)) { c = InputStream.class; + } else if (File.class.isAssignableFrom(c)) { + c = File.class; } argsClz[i++] = c; } diff --git a/src/java/org/apache/poi/ss/util/CellReference.java b/src/java/org/apache/poi/ss/util/CellReference.java index d14670221f..3ef32e30f6 100644 --- a/src/java/org/apache/poi/ss/util/CellReference.java +++ b/src/java/org/apache/poi/ss/util/CellReference.java @@ -132,7 +132,7 @@ public class CellReference { if (rowRef.length() == 0) { _rowIndex = -1; } else { - // throws NumberFormatException if rowRef is not convertable to an int + // throws NumberFormatException if rowRef is not convertible to an int _rowIndex = Integer.parseInt(rowRef)-1; // -1 to convert 1-based to zero-based } } @@ -336,10 +336,10 @@ public class CellReference { if(colStr.toUpperCase(Locale.ROOT).compareTo(lastCol) > 0) { return false; } - } else { + } /*else { // apparent column name has less chars than max // no need to check range - } + }*/ return true; } @@ -426,7 +426,7 @@ public class CellReference { // AreaReference.separateAreaRefs() // SheetNameFormatter.format() (inverse) - StringBuffer sb = new StringBuffer(indexOfSheetNameDelimiter); + StringBuilder sb = new StringBuilder(indexOfSheetNameDelimiter); for(int i=1; i<lastQuotePos; i++) { // Note boundaries - skip outer quotes char ch = reference.charAt(i); @@ -495,11 +495,7 @@ public class CellReference { @Override public String toString() { - StringBuffer sb = new StringBuffer(64); - sb.append(getClass().getName()).append(" ["); - sb.append(formatAsString()); - sb.append("]"); - return sb.toString(); + return getClass().getName() + " [" + formatAsString() + "]"; } /** diff --git a/src/java/org/apache/poi/ss/util/CellUtil.java b/src/java/org/apache/poi/ss/util/CellUtil.java index 70dcffc289..3bcef4beb4 100644 --- a/src/java/org/apache/poi/ss/util/CellUtil.java +++ b/src/java/org/apache/poi/ss/util/CellUtil.java @@ -420,7 +420,7 @@ public final class CellUtil { } /** - * Utility method that returns the named short value form the given map. + * Utility method that returns the named short value from the given map. * * @param properties map of named properties (String -> Object) * @param name property name @@ -436,7 +436,7 @@ public final class CellUtil { } /** - * Utility method that returns the named int value form the given map. + * Utility method that returns the named int value from the given map. * * @param properties map of named properties (String -> Object) * @param name property name @@ -452,7 +452,7 @@ public final class CellUtil { } /** - * Utility method that returns the named BorderStyle value form the given map. + * Utility method that returns the named BorderStyle value from the given map. * * @param properties map of named properties (String -> Object) * @param name property name @@ -483,7 +483,7 @@ public final class CellUtil { } /** - * Utility method that returns the named FillPatternType value form the given map. + * Utility method that returns the named FillPatternType value from the given map. * * @param properties map of named properties (String -> Object) * @param name property name @@ -515,7 +515,7 @@ public final class CellUtil { } /** - * Utility method that returns the named HorizontalAlignment value form the given map. + * Utility method that returns the named HorizontalAlignment value from the given map. * * @param properties map of named properties (String -> Object) * @param name property name @@ -547,7 +547,7 @@ public final class CellUtil { } /** - * Utility method that returns the named VerticalAlignment value form the given map. + * Utility method that returns the named VerticalAlignment value from the given map. * * @param properties map of named properties (String -> Object) * @param name property name @@ -579,7 +579,7 @@ public final class CellUtil { } /** - * Utility method that returns the named boolean value form the given map. + * Utility method that returns the named boolean value from the given map. * * @param properties map of properties (String -> Object) * @param name property name @@ -594,7 +594,7 @@ public final class CellUtil { } return false; } - + /** * Utility method that puts the given value to the given map. * diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java index 035c6ee762..3dd4cb0d57 100644 --- a/src/java/org/apache/poi/util/IOUtils.java +++ b/src/java/org/apache/poi/util/IOUtils.java @@ -571,6 +571,22 @@ public final class IOUtils { return new byte[(int)length]; } + /** + * Simple utility function to check that you haven't hit EOF + * when reading a byte. + * + * @param is inputstream to read + * @return byte read, unless + * @throws IOException on IOException or EOF if -1 is read + */ + public static int readByte(InputStream is) throws IOException { + int b = is.read(); + if (b == -1) { + throw new EOFException(); + } + return b; + } + private static void throwRFE(long length, int maxLength) { throw new RecordFormatException("Tried to allocate an array of length "+length + ", but "+ maxLength+" is the maximum for this record type.\n" + diff --git a/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java b/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java index 5a368c576a..54fa790eca 100644 --- a/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java +++ b/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java @@ -42,6 +42,7 @@ import org.apache.poi.util.POILogger; import org.apache.poi.xddf.usermodel.chart.XDDFChart; import org.apache.poi.xssf.usermodel.XSSFRelation; import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xwpf.usermodel.XWPFRelation; /** * Represents an entry of a OOXML package. @@ -613,6 +614,14 @@ public class POIXMLDocumentPart { */ protected void read(POIXMLFactory factory, Map<PackagePart, POIXMLDocumentPart> context) throws OpenXML4JException { PackagePart pp = getPackagePart(); + + if (pp.getContentType().equals(XWPFRelation.TEMPLATE.getContentType())) { + logger.log(POILogger.WARN, + "POI does not currently support template.main+xml (glossary) parts. " + + "Skipping this part for now."); + return; + } + // add mapping a second time, in case of initial caller hasn't done so POIXMLDocumentPart otherChild = context.put(pp, this); if (otherChild != null && otherChild != this) { diff --git a/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java index 832d47623b..0cf0cfbc19 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/exceptions/InvalidFormatException.java @@ -18,7 +18,7 @@ package org.apache.poi.openxml4j.exceptions; @SuppressWarnings("serial") -public final class InvalidFormatException extends OpenXML4JException{ +public final class InvalidFormatException extends OpenXML4JException { public InvalidFormatException(String message){ super(message); diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java index 9625a86ff9..767abc239d 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java @@ -291,6 +291,8 @@ public abstract class OPCPackage implements RelationshipSource, Closeable { * @param in * The InputStream to read the package from * @return A PackageBase object + * + * @throws InvalidFormatException */ public static OPCPackage open(InputStream in) throws InvalidFormatException, IOException { diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java index 9fa93f7a06..8e276f896e 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java @@ -63,7 +63,7 @@ import org.w3c.dom.events.EventListener; * This class bundles the configuration options used for the existing * signature facets. * Apart of the thread local members (e.g. opc-package) most values will probably be constant, so - * it might be configured centrally (e.g. by spring) + * it might be configured centrally (e.g. by spring) */ @SuppressWarnings({"unused","WeakerAccess"}) public class SignatureConfig { @@ -76,14 +76,14 @@ public class SignatureConfig { public interface SignatureConfigurable { - void setSignatureConfig(SignatureConfig signatureConfig); + void setSignatureConfig(SignatureConfig signatureConfig); } private ThreadLocal<OPCPackage> opcPackage = new ThreadLocal<>(); private ThreadLocal<XMLSignatureFactory> signatureFactory = new ThreadLocal<>(); private ThreadLocal<KeyInfoFactory> keyInfoFactory = new ThreadLocal<>(); private ThreadLocal<Provider> provider = new ThreadLocal<>(); - + private List<SignatureFacet> signatureFacets = new ArrayList<>(); private HashAlgorithm digestAlgo = HashAlgorithm.sha256; private Date executionTime = new Date(); @@ -96,11 +96,11 @@ public class SignatureConfig { private SignaturePolicyService signaturePolicyService; private URIDereferencer uriDereferencer; private String canonicalizationMethod = CanonicalizationMethod.INCLUSIVE; - + private boolean includeEntireCertificateChain = true; private boolean includeIssuerSerial; private boolean includeKeyValue; - + /** * the time-stamp service used for XAdES-T and XAdES-X. */ @@ -123,7 +123,7 @@ public class SignatureConfig { private String tspRequestPolicy = "1.3.6.1.4.1.13762.3"; private String userAgent = "POI XmlSign Service TSP Client"; private String proxyUrl; - + /** * the optional revocation data service used for XAdES-C and XAdES-X-L. * When <code>null</code> the signature will be limited to XAdES-T only. @@ -148,16 +148,16 @@ public class SignatureConfig { * <code>null</code> value will trigger an automatically generated signature Id. */ private String packageSignatureId = "idPackageSignature"; - + /** * Gives back the human-readable description of what the citizen will be * signing. The default value is "Office OpenXML Document". */ private String signatureDescription = "Office OpenXML Document"; - + /** * The process of signing includes the marshalling of xml structures. - * This also includes the canonicalization. Currently this leads to problems + * This also includes the canonicalization. Currently this leads to problems * with certain namespaces, so this EventListener is used to interfere * with the marshalling process. */ @@ -177,7 +177,7 @@ public class SignatureConfig { /** * Inits and checks the config object. - * If not set previously, complex configuration properties also get + * If not set previously, complex configuration properties also get * created/initialized via this initialization call. * * @param onlyValidation if true, only a subset of the properties @@ -202,7 +202,7 @@ public class SignatureConfig { namespacePrefixes.put(OO_DIGSIG_NS, "mdssi"); namespacePrefixes.put(XADES_132_NS, "xd"); } - + if (onlyValidation) { return; } @@ -210,15 +210,15 @@ public class SignatureConfig { if (signatureMarshalListener == null) { signatureMarshalListener = new SignatureMarshalListener(); } - + if (signatureMarshalListener instanceof SignatureConfigurable) { ((SignatureConfigurable)signatureMarshalListener).setSignatureConfig(this); } - + if (tspService != null) { tspService.setSignatureConfig(this); } - + if (signatureFacets.isEmpty()) { addSignatureFacet(new OOXMLSignatureFacet()); addSignatureFacet(new KeyInfoSignatureFacet()); @@ -230,14 +230,14 @@ public class SignatureConfig { sf.setSignatureConfig(this); } } - + /** - * @param signatureFacet the signature facet is appended to facet list + * @param signatureFacet the signature facet is appended to facet list */ public void addSignatureFacet(SignatureFacet signatureFacet) { signatureFacets.add(signatureFacet); } - + /** * @return the list of facets, may be empty when the config object is not initialized */ @@ -265,14 +265,14 @@ public class SignatureConfig { public void setDigestAlgo(HashAlgorithm digestAlgo) { this.digestAlgo = digestAlgo; } - + /** * @return the opc package to be used by this thread, stored as thread-local */ public OPCPackage getOpcPackage() { return opcPackage.get(); } - + /** * @param opcPackage the opc package to be handled by this thread, stored as thread-local */ @@ -398,14 +398,14 @@ public class SignatureConfig { public void setSignatureDescription(String signatureDescription) { this.signatureDescription = signatureDescription; } - + /** * @return the default canonicalization method, defaults to INCLUSIVE */ public String getCanonicalizationMethod() { return canonicalizationMethod; } - + /** * @param canonicalizationMethod the default canonicalization method */ @@ -459,15 +459,15 @@ public class SignatureConfig { public void setTspUrl(String tspUrl) { this.tspUrl = tspUrl; } - + /** * @return if true, uses timestamp-request/response mimetype, - * if false, timestamp-query/reply mimetype + * if false, timestamp-query/reply mimetype */ public boolean isTspOldProtocol() { return tspOldProtocol; } - + /** * @param tspOldProtocol defines the timestamp-protocol mimetype * @see #isTspOldProtocol @@ -475,7 +475,7 @@ public class SignatureConfig { public void setTspOldProtocol(boolean tspOldProtocol) { this.tspOldProtocol = tspOldProtocol; } - + /** * @return the hash algorithm to be used for the timestamp entry. * Defaults to the hash algorithm of the main entry @@ -483,7 +483,7 @@ public class SignatureConfig { public HashAlgorithm getTspDigestAlgo() { return nvl(tspDigestAlgo,digestAlgo); } - + /** * @param tspDigestAlgo the algorithm to be used for the timestamp entry. * if <code>null</code>, the hash algorithm of the main entry @@ -499,7 +499,7 @@ public class SignatureConfig { public String getProxyUrl() { return proxyUrl; } - + /** * @param proxyUrl the proxy url to be used for all communications. * Currently this affects the timestamp service @@ -507,56 +507,56 @@ public class SignatureConfig { public void setProxyUrl(String proxyUrl) { this.proxyUrl = proxyUrl; } - + /** * @return the timestamp service. Defaults to {@link TSPTimeStampService} */ public TimeStampService getTspService() { return tspService; } - + /** * @param tspService the timestamp service */ public void setTspService(TimeStampService tspService) { this.tspService = tspService; } - + /** * @return the user id for the timestamp service - currently only basic authorization is supported */ public String getTspUser() { return tspUser; } - + /** * @param tspUser the user id for the timestamp service - currently only basic authorization is supported */ public void setTspUser(String tspUser) { this.tspUser = tspUser; } - + /** * @return the password for the timestamp service */ public String getTspPass() { return tspPass; } - + /** * @param tspPass the password for the timestamp service */ public void setTspPass(String tspPass) { this.tspPass = tspPass; } - + /** * @return the validator for the timestamp service (certificate) */ public TimeStampServiceValidator getTspValidator() { return tspValidator; } - + /** * @param tspValidator the validator for the timestamp service (certificate) */ @@ -586,7 +586,7 @@ public class SignatureConfig { public HashAlgorithm getXadesDigestAlgo() { return nvl(xadesDigestAlgo,digestAlgo); } - + /** * @param xadesDigestAlgo hash algorithm used for XAdES. * When <code>null</code>, defaults to {@link #getDigestAlgo()} @@ -611,7 +611,7 @@ public class SignatureConfig { public String getUserAgent() { return userAgent; } - + /** * @param userAgent the user agent used for http communication (e.g. to the TSP) */ @@ -626,7 +626,7 @@ public class SignatureConfig { public String getTspRequestPolicy() { return tspRequestPolicy; } - + /** * @param tspRequestPolicy the asn.1 object id for the tsp request policy. */ @@ -636,7 +636,7 @@ public class SignatureConfig { /** * @return true, if the whole certificate chain is included in the signature. - * When false, only the signer cert will be included + * When false, only the signer cert will be included */ public boolean isIncludeEntireCertificateChain() { return includeEntireCertificateChain; @@ -728,7 +728,7 @@ public class SignatureConfig { * Make sure the DN is encoded using the same order as present * within the certificate. This is an Office2010 work-around. * Should be reverted back. - * + * * XXX: not correct according to RFC 4514. * * @return when true, the issuer DN is used instead of the issuer X500 principal @@ -744,7 +744,7 @@ public class SignatureConfig { this.xadesIssuerNameNoReverseOrder = xadesIssuerNameNoReverseOrder; } - + /** * @return the event listener which is active while xml structure for * the signature is created. @@ -813,8 +813,8 @@ public class SignatureConfig { /** * Converts the digest algorithm - currently only sha* and ripemd160 is supported. - * MS Office only supports sha1, sha256, sha384, sha512. - * + * MS Office only supports sha1, sha256, sha384, sha512. + * * @param digestAlgo the digest algorithm * @return the uri for the given digest */ @@ -835,7 +835,7 @@ public class SignatureConfig { * Converts the digest algorithm ur - currently only sha* and ripemd160 is supported. * MS Office only supports sha1, sha256, sha384, sha512. * - * @param digestAlgo the digest algorithm uri + * @param digestMethodUri the digest algorithm uri * @return the hash algorithm for the given digest */ private static HashAlgorithm getDigestMethodAlgo(String digestMethodUri) { @@ -895,7 +895,7 @@ public class SignatureConfig { public void setSignatureFactory(XMLSignatureFactory signatureFactory) { this.signatureFactory.set(signatureFactory); } - + /** * @return the xml signature factory (thread-local) */ @@ -914,7 +914,7 @@ public class SignatureConfig { public void setKeyInfoFactory(KeyInfoFactory keyInfoFactory) { this.keyInfoFactory.set(keyInfoFactory); } - + /** * @return the key factory (thread-local) */ @@ -934,10 +934,10 @@ public class SignatureConfig { * <li>the Santuario xmlsec provider</li> * <li>the JDK xmlsec provider</li> * </ul> - * + * * For signing the classes are linked against the Santuario xmlsec, so this might * only work for validation (not tested). - * + * * @return the xml dsig provider */ public Provider getProvider() { @@ -948,7 +948,7 @@ public class SignatureConfig { // Santuario xmlsec "org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI", // JDK xmlsec - "org.jcp.xml.dsig.internal.dom.XMLDSigRI" + "org.jcp.xml.dsig.internal.dom.XMLDSigRI" }; for (String pn : dsigProviderNames) { if (pn == null) { @@ -966,7 +966,7 @@ public class SignatureConfig { if (prov == null) { throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!"); } - + return prov; } diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java index c2b22de4f2..0427817fdd 100644 --- a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java @@ -116,13 +116,15 @@ public class XDGFShape extends XDGFSheet { _parentPage = parentPage; TextType text = shapeSheet.getText(); - if (text != null) + if (text != null) { _text = new XDGFText(text, this); + } if (shapeSheet.isSetShapes()) { _shapes = new ArrayList<>(); - for (ShapeSheetType shape : shapeSheet.getShapes().getShapeArray()) + for (ShapeSheetType shape : shapeSheet.getShapes().getShapeArray()) { _shapes.add(new XDGFShape(this, shape, parentPage, document)); + } } readProperties(); @@ -130,10 +132,11 @@ public class XDGFShape extends XDGFSheet { @Override public String toString() { - if (_parentPage instanceof XDGFMasterContents) + if (_parentPage instanceof XDGFMasterContents) { return _parentPage + ": <Shape ID=\"" + getID() + "\">"; - else + } else { return "<Shape ID=\"" + getID() + "\">"; + } } protected void readProperties() { @@ -181,9 +184,10 @@ public class XDGFShape extends XDGFSheet { if (obj.isSetMaster()) { _master = pageContents.getMasterById(obj.getMaster()); - if (_master == null) + if (_master == null) { throw XDGFException.error("refers to non-existant master " + obj.getMaster(), this); + } /* * If a master has one top-level shape, a shape that inherits from @@ -209,11 +213,12 @@ public class XDGFShape extends XDGFSheet { } } else if (obj.isSetMasterShape()) { - _masterShape = master.getShapeById(obj.getMasterShape()); - if (_masterShape == null) + _masterShape = (master == null) ? null : master.getShapeById(obj.getMasterShape()); + if (_masterShape == null) { throw XDGFException.error( "refers to non-existant master shape " + obj.getMasterShape(), this); + } } @@ -229,21 +234,24 @@ public class XDGFShape extends XDGFSheet { protected void setupSectionMasters() { - if (_masterShape == null) + if (_masterShape == null) { return; + } try { for (Entry<String, XDGFSection> section : _sections.entrySet()) { XDGFSection master = _masterShape.getSection(section.getKey()); - if (master != null) + if (master != null) { section.getValue().setupMaster(master); + } } for (Entry<Long, GeometrySection> section : _geometry.entrySet()) { GeometrySection master = _masterShape.getGeometryByIdx(section .getKey()); - if (master != null) + if (master != null) { section.getValue().setupMaster(master); + } } } catch (POIXMLException e) { throw XDGFException.wrap(this.toString(), e); @@ -266,8 +274,9 @@ public class XDGFShape extends XDGFSheet { public String getTextAsString() { XDGFText text = getText(); - if (text == null) + if (text == null) { return ""; + } return text.getTextContent(); } @@ -294,7 +303,7 @@ public class XDGFShape extends XDGFSheet { } /** - * Only available if this shape is a shape group, may be null + * Only available if this shape is a shape group, may be null */ // -> May be null public List<XDGFShape> getShapes() { @@ -304,28 +313,32 @@ public class XDGFShape extends XDGFSheet { // unique to this shape on the page? public String getName() { String name = getXmlObject().getName(); - if (name == null) + if (name == null) { return ""; + } return name; } // unique to this shape on the page? public String getShapeType() { String type = getXmlObject().getType(); - if (type == null) + if (type == null) { return ""; + } return type; } // name of the symbol that this was derived from public String getSymbolName() { - if (_master == null) + if (_master == null) { return ""; + } String name = _master.getName(); - if (name == null) + if (name == null) { return ""; + } return name; } @@ -345,8 +358,9 @@ public class XDGFShape extends XDGFSheet { XDGFShape top = null; if (_parent != null) { top = _parent.getTopmostParentShape(); - if (top == null) + if (top == null) { top = _parent; + } } return top; @@ -381,190 +395,223 @@ public class XDGFShape extends XDGFSheet { } public XDGFText getText() { - if (_text == null && _masterShape != null) + if (_text == null && _masterShape != null) { return _masterShape.getText(); + } return _text; } public Double getPinX() { - if (_pinX == null && _masterShape != null) + if (_pinX == null && _masterShape != null) { return _masterShape.getPinX(); + } - if (_pinX == null) + if (_pinX == null) { throw XDGFException.error("PinX not set!", this); + } return _pinX; } public Double getPinY() { - if (_pinY == null && _masterShape != null) + if (_pinY == null && _masterShape != null) { return _masterShape.getPinY(); + } - if (_pinY == null) + if (_pinY == null) { throw XDGFException.error("PinY not specified!", this); + } return _pinY; } public Double getWidth() { - if (_width == null && _masterShape != null) + if (_width == null && _masterShape != null) { return _masterShape.getWidth(); + } - if (_width == null) + if (_width == null) { throw XDGFException.error("Width not specified!", this); + } return _width; } public Double getHeight() { - if (_height == null && _masterShape != null) + if (_height == null && _masterShape != null) { return _masterShape.getHeight(); + } - if (_height == null) + if (_height == null) { throw XDGFException.error("Height not specified!", this); + } return _height; } public Double getLocPinX() { - if (_locPinX == null && _masterShape != null) + if (_locPinX == null && _masterShape != null) { return _masterShape.getLocPinX(); + } - if (_locPinX == null) + if (_locPinX == null) { throw XDGFException.error("LocPinX not specified!", this); + } return _locPinX; } public Double getLocPinY() { - if (_locPinY == null && _masterShape != null) + if (_locPinY == null && _masterShape != null) { return _masterShape.getLocPinY(); + } - if (_locPinY == null) + if (_locPinY == null) { throw XDGFException.error("LocPinY not specified!", this); + } return _locPinY; } public Double getBeginX() { - if (_beginX == null && _masterShape != null) + if (_beginX == null && _masterShape != null) { return _masterShape.getBeginX(); + } return _beginX; } public Double getBeginY() { - if (_beginY == null && _masterShape != null) + if (_beginY == null && _masterShape != null) { return _masterShape.getBeginY(); + } return _beginY; } public Double getEndX() { - if (_endX == null && _masterShape != null) + if (_endX == null && _masterShape != null) { return _masterShape.getEndX(); + } return _endX; } public Double getEndY() { - if (_endY == null && _masterShape != null) + if (_endY == null && _masterShape != null) { return _masterShape.getEndY(); + } return _endY; } public Double getAngle() { - if (_angle == null && _masterShape != null) + if (_angle == null && _masterShape != null) { return _masterShape.getAngle(); + } return _angle; } public Boolean getFlipX() { - if (_flipX == null && _masterShape != null) + if (_flipX == null && _masterShape != null) { return _masterShape.getFlipX(); + } return _flipX; } public Boolean getFlipY() { - if (_flipY == null && _masterShape != null) + if (_flipY == null && _masterShape != null) { return _masterShape.getFlipY(); + } return _flipY; } public Double getTxtPinX() { if (_txtPinX == null && _masterShape != null - && _masterShape._txtPinX != null) + && _masterShape._txtPinX != null) { return _masterShape._txtPinX; + } - if (_txtPinX == null) + if (_txtPinX == null) { return getWidth() * 0.5; + } return _txtPinX; } public Double getTxtPinY() { if (_txtLocPinY == null && _masterShape != null - && _masterShape._txtLocPinY != null) + && _masterShape._txtLocPinY != null) { return _masterShape._txtLocPinY; + } - if (_txtPinY == null) + if (_txtPinY == null) { return getHeight() * 0.5; + } return _txtPinY; } public Double getTxtLocPinX() { if (_txtLocPinX == null && _masterShape != null - && _masterShape._txtLocPinX != null) + && _masterShape._txtLocPinX != null) { return _masterShape._txtLocPinX; + } - if (_txtLocPinX == null) + if (_txtLocPinX == null) { return getTxtWidth() * 0.5; + } return _txtLocPinX; } public Double getTxtLocPinY() { if (_txtLocPinY == null && _masterShape != null - && _masterShape._txtLocPinY != null) + && _masterShape._txtLocPinY != null) { return _masterShape._txtLocPinY; + } - if (_txtLocPinY == null) + if (_txtLocPinY == null) { return getTxtHeight() * 0.5; + } return _txtLocPinY; } public Double getTxtAngle() { - if (_txtAngle == null && _masterShape != null) + if (_txtAngle == null && _masterShape != null) { return _masterShape.getTxtAngle(); + } return _txtAngle; } public Double getTxtWidth() { if (_txtWidth == null && _masterShape != null - && _masterShape._txtWidth != null) + && _masterShape._txtWidth != null) { return _masterShape._txtWidth; + } - if (_txtWidth == null) + if (_txtWidth == null) { return getWidth(); + } return _txtWidth; } public Double getTxtHeight() { if (_txtHeight == null && _masterShape != null - && _masterShape._txtHeight != null) + && _masterShape._txtHeight != null) { return _masterShape._txtHeight; + } - if (_txtHeight == null) + if (_txtHeight == null) { return getHeight(); + } return _txtHeight; } @@ -573,8 +620,9 @@ public class XDGFShape extends XDGFSheet { public Integer getLineCap() { Integer lineCap = super.getLineCap(); - if (lineCap != null) + if (lineCap != null) { return lineCap; + } // get from master if (_masterShape != null) { @@ -589,8 +637,9 @@ public class XDGFShape extends XDGFSheet { public Color getLineColor() { Color lineColor = super.getLineColor(); - if (lineColor != null) + if (lineColor != null) { return lineColor; + } // get from master if (_masterShape != null) { @@ -605,8 +654,9 @@ public class XDGFShape extends XDGFSheet { public Integer getLinePattern() { Integer linePattern = super.getLinePattern(); - if (linePattern != null) + if (linePattern != null) { return linePattern; + } // get from master if (_masterShape != null) { @@ -621,8 +671,9 @@ public class XDGFShape extends XDGFSheet { public Double getLineWeight() { Double lineWeight = super.getLineWeight(); - if (lineWeight != null) + if (lineWeight != null) { return lineWeight; + } // get from master if (_masterShape != null) { @@ -637,8 +688,9 @@ public class XDGFShape extends XDGFSheet { public Color getFontColor() { Color fontColor = super.getFontColor(); - if (fontColor != null) + if (fontColor != null) { return fontColor; + } // get from master if (_masterShape != null) { @@ -653,8 +705,9 @@ public class XDGFShape extends XDGFSheet { public Double getFontSize() { Double fontSize = super.getFontSize(); - if (fontSize != null) + if (fontSize != null) { return fontSize; + } // get from master if (_masterShape != null) { @@ -791,11 +844,11 @@ public class XDGFShape extends XDGFSheet { public Rectangle2D.Double getBounds() { return new Rectangle2D.Double(0, 0, getWidth(), getHeight()); } - + /** * @return returns bounds as a path in local coordinates, which is * userful if you need to transform to global coordinates - * + * * Warning: Don't use this for 1d objects, and will fail for * infinite line objects */ @@ -819,8 +872,9 @@ public class XDGFShape extends XDGFSheet { */ public Path2D.Double getPath() { for (GeometrySection geoSection : getGeometrySections()) { - if (geoSection.getNoShow()) + if (geoSection.getNoShow()) { continue; + } return geoSection.getPath(this); } @@ -833,8 +887,9 @@ public class XDGFShape extends XDGFSheet { */ public boolean hasGeometry() { for (GeometrySection geoSection : getGeometrySections()) { - if (!geoSection.getNoShow()) + if (!geoSection.getNoShow()) { return true; + } } return false; } @@ -889,8 +944,9 @@ public class XDGFShape extends XDGFSheet { tr.concatenate(getParentTransform()); try { - if (visitor.accept(this)) + if (visitor.accept(this)) { visitor.visit(this, tr, level); + } if (_shapes != null) { for (XDGFShape shape : _shapes) { @@ -914,8 +970,9 @@ public class XDGFShape extends XDGFSheet { public void visitShapes(ShapeVisitor visitor, int level) { try { - if (visitor.accept(this)) + if (visitor.accept(this)) { visitor.visit(this, null, level); + } if (_shapes != null) { for (XDGFShape shape : _shapes) { diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java index f3b24f9054..c823bb343d 100644 --- a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java @@ -52,7 +52,7 @@ public class XDGFText { // is a mixed type) return ((TextTypeImpl) _text).getStringValue(); } - + /** * These are in the shape coordinate system * @@ -82,8 +82,8 @@ public class XDGFText { public Path2D.Double getBoundsAsPath() { Rectangle2D.Double rect = getTextBounds(); - Double w = rect.getWidth(); - Double h = rect.getHeight(); + double w = rect.getWidth(); + double h = rect.getHeight(); Path2D.Double bounds = new Path2D.Double(); bounds.moveTo(0, 0); @@ -94,7 +94,7 @@ public class XDGFText { return bounds; } - + /** * @return Center of text in local coordinates */ @@ -110,8 +110,9 @@ public class XDGFText { public void draw(Graphics2D graphics) { String textContent = getTextContent(); - if (textContent.length() == 0) + if (textContent.length() == 0) { return; + } Rectangle2D.Double bounds = getTextBounds(); @@ -140,22 +141,25 @@ public class XDGFText { } Double txtAngle = _parent.getTxtAngle(); - if (txtAngle != null && Math.abs(txtAngle) > 0.01) + if (txtAngle != null && Math.abs(txtAngle) > 0.01) { graphics.rotate(txtAngle); + } float nextY = 0; for (String line : lines) { - if (line.length() == 0) + if (line.length() == 0) { continue; + } TextLayout layout = new TextLayout(line, font, frc); - if (layout.isLeftToRight()) + if (layout.isLeftToRight()) { layout.draw(graphics, 0, nextY); - else + } else { layout.draw(graphics, (float) (bounds.width - layout.getAdvance()), nextY); + } nextY += layout.getAscent() + layout.getDescent() + layout.getLeading(); diff --git a/src/ooxml/java/org/apache/poi/xdgf/util/HierarchyPrinter.java b/src/ooxml/java/org/apache/poi/xdgf/util/HierarchyPrinter.java index 92fbcb0d2c..eb37159ba4 100644 --- a/src/ooxml/java/org/apache/poi/xdgf/util/HierarchyPrinter.java +++ b/src/ooxml/java/org/apache/poi/xdgf/util/HierarchyPrinter.java @@ -22,6 +22,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; @@ -38,17 +39,17 @@ import org.apache.poi.xdgf.usermodel.shape.ShapeVisitor; public class HierarchyPrinter { public static void printHierarchy(XDGFPage page, File outDir) - throws FileNotFoundException, UnsupportedEncodingException { + throws FileNotFoundException, UnsupportedEncodingException, IOException { File pageFile = new File(outDir, "page" + page.getPageNumber() + "-" + Util.sanitizeFilename(page.getName()) + ".txt"); - OutputStream os = new FileOutputStream(pageFile); - PrintStream pos = new PrintStream(os, false, "utf-8"); - - printHierarchy(page, pos); - - pos.close(); + try ( + OutputStream os = new FileOutputStream(pageFile); + PrintStream pos = new PrintStream(os, false, "utf-8") + ) { + printHierarchy(page, pos); + } } public static void printHierarchy(XDGFPage page, final PrintStream os) { @@ -71,7 +72,7 @@ public class HierarchyPrinter { } public static void printHierarchy(XmlVisioDocument document, - String outDirname) throws FileNotFoundException, UnsupportedEncodingException { + String outDirname) throws FileNotFoundException, UnsupportedEncodingException, IOException { File outDir = new File(outDirname); @@ -89,8 +90,9 @@ public class HierarchyPrinter { String inFilename = args[0]; String outDir = args[1]; - XmlVisioDocument doc = new XmlVisioDocument(new FileInputStream( - inFilename)); - printHierarchy(doc, outDir); + try (FileInputStream is = new FileInputStream(inFilename)) { + XmlVisioDocument doc = new XmlVisioDocument(is); + printHierarchy(doc, outDir); + } } } diff --git a/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java b/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java index c0ddf5fb0b..e8c96c3355 100644 --- a/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java +++ b/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java @@ -21,7 +21,10 @@ import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import javax.imageio.ImageIO; @@ -33,7 +36,7 @@ import org.apache.poi.xdgf.usermodel.shape.ShapeRenderer; /** * Converts a Visio diagram to a PNG file. - * + * * As more elements and styles are added/supported the output will get * better, but it's very rough right now. */ @@ -91,11 +94,8 @@ public class VsdxToPng { graphics.dispose(); - OutputStream out = new FileOutputStream(outFile); - try { + try (FileOutputStream out = new FileOutputStream(outFile)) { ImageIO.write(img, "png", out); - } finally { - out.close(); } } @@ -127,8 +127,9 @@ public class VsdxToPng { renderer = new ShapeDebuggerRenderer(); } - XmlVisioDocument doc = new XmlVisioDocument(new FileInputStream( - inFilename)); - renderToPng(doc, pngDir, 2000 / 11.0, renderer); + try (FileInputStream is = new FileInputStream(inFilename)) { + XmlVisioDocument doc = new XmlVisioDocument(is); + renderToPng(doc, pngDir, 2000 / 11.0, renderer); + } } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index 4019030761..687ddc5f5b 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -36,6 +36,7 @@ import org.apache.poi.ooxml.POIXMLDocument; import org.apache.poi.ooxml.POIXMLDocumentPart; import org.apache.poi.ooxml.POIXMLException; import org.apache.poi.ooxml.extractor.POIXMLPropertiesTextExtractor; +import org.apache.poi.ooxml.util.PackageHelper; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackagePart; @@ -50,7 +51,6 @@ import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; -import org.apache.poi.ooxml.util.PackageHelper; import org.apache.poi.util.Units; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; @@ -362,7 +362,7 @@ public class XMLSlideShow extends POIXMLDocument CTNotesMasterIdListEntry notesMasterId = notesMasterIdList.addNewNotesMasterId(); notesMasterId.setId(rp.getRelationship().getId()); - Integer themeIndex = 1; + int themeIndex = 1; // TODO: check if that list can be replaced by idx = Math.max(idx,themeIdx) List<Integer> themeIndexList = new ArrayList<>(); for (POIXMLDocumentPart p : getRelations()) { @@ -626,7 +626,7 @@ public class XMLSlideShow extends POIXMLDocument // TODO: implement! throw new UnsupportedOperationException(); } - + @Override public POIXMLPropertiesTextExtractor getMetadataTextExtractor() { return new POIXMLPropertiesTextExtractor(this); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java index d7aed091b1..50b82df3b0 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java @@ -48,7 +48,7 @@ import org.w3c.dom.Node; @Internal public class XSLFColor { private final static POILogger LOGGER = POILogFactory.getLogger(XSLFColor.class); - + private XmlObject _xmlObject; private Color _color; private CTSchemeColor _phClr; @@ -128,7 +128,7 @@ public class XSLFColor { } }; } - + private Color toColor(XmlObject obj, XSLFTheme theme) { Color color = null; for (XmlObject ch : obj.selectPath("*")) { @@ -207,19 +207,19 @@ public class XSLFColor { if (fill.isSetScrgbClr()) { fill.unsetScrgbClr(); } - + if (fill.isSetHslClr()) { fill.unsetHslClr(); } - + if (fill.isSetPrstClr()) { fill.unsetPrstClr(); } - + if (fill.isSetSchemeClr()) { fill.unsetSchemeClr(); } - + if (fill.isSetSysClr()) { fill.unsetSysClr(); } @@ -227,12 +227,12 @@ public class XSLFColor { float[] rgbaf = color.getRGBComponents(null); boolean addAlpha = (rgbaf.length == 4 && rgbaf[3] < 1f); CTPositiveFixedPercentage alphaPct; - + // see office open xml part 4 - 5.1.2.2.30 and 5.1.2.2.32 if (isInt(rgbaf[0]) && isInt(rgbaf[1]) && isInt(rgbaf[2])) { // sRGB has a gamma of 2.2 CTSRgbColor rgb = fill.addNewSrgbClr(); - + byte rgbBytes[] = { (byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue() }; rgb.setVal(rgbBytes); alphaPct = (addAlpha) ? rgb.addNewAlpha() : null; @@ -249,14 +249,14 @@ public class XSLFColor { alphaPct.setVal((int)(100000 * rgbaf[3])); } } - + /** * @return true, if this is an integer color value */ private static boolean isInt(float f) { - return Math.abs((f*255f) - Math.rint(f*255f)) < 0.00001f; + return Math.abs((f*255d) - Math.rint(f*255d)) < 0.00001; } - + private int getRawValue(String elem) { String query = "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' $this//a:" + elem; @@ -281,9 +281,9 @@ public class XSLFColor { } } - return -1; + return -1; } - + /** * Read a perecentage value from the supplied xml bean. * Example: @@ -305,7 +305,7 @@ public class XSLFColor { * or -1 if the value is not set */ int getAlpha(){ - return getPercentageValue("alpha"); + return getPercentageValue("alpha"); } /** @@ -413,7 +413,7 @@ public class XSLFColor { /** * specifies the input color with the specific red component, but with the blue and green color * components unchanged - * + * * @return the value of the red component specified as a * percentage with 0% indicating minimal blue and 100% indicating maximum * or -1 if the value is not set @@ -479,7 +479,7 @@ public class XSLFColor { /** * specifies a darker version of its input color. * A 10% shade is 10% of the input color combined with 90% black. - * + * * @return the value of the shade specified as a * percentage with 0% indicating minimal shade and 100% indicating maximum * or -1 if the value is not set diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java index 64b92a9bbf..72d19e7aee 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java @@ -49,9 +49,7 @@ implements Notes<XSLFShape,XSLFTextParagraph> { * * @param part the package part holding the notes data, * the content type must be <code>application/vnd.openxmlformats-officedocument.notes+xml</code> - * @param rel the package relationship holding this notes, - * the relationship type must be http://schemas.openxmlformats.org/officeDocument/2006/relationships/notes - * + * * @since POI 3.14-Beta1 */ XSLFNotes(PackagePart part) throws IOException, XmlException { @@ -77,7 +75,7 @@ implements Notes<XSLFShape,XSLFTextParagraph> { @Override protected String getRootElementName(){ - return "notes"; + return "notes"; } @Override diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java index 4d162027e8..aded851db5 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java @@ -70,7 +70,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; @Beta public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { static final String PML_NS = "http://schemas.openxmlformats.org/presentationml/2006/main"; - + private final XmlObject _shape; private final XSLFSheet _sheet; private XSLFShapeContainer _parent; @@ -82,7 +82,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { _shape = shape; _sheet = sheet; } - + /** * @return the xml bean holding this shape's data */ @@ -91,11 +91,12 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { // the (not existing) xmlbeans hierarchy and subclasses shouldn't narrow it's return value return _shape; } - + + @Override public XSLFSheet getSheet() { return _sheet; } - + @Override public String getShapeName(){ return getCNvPr().getName(); @@ -124,22 +125,24 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { PlaceableShape<?,?> ps = (PlaceableShape<?,?>)this; ps.setAnchor(sh.getAnchor()); } - - + + } - + public void setParent(XSLFShapeContainer parent) { this._parent = parent; } - + + @Override public XSLFShapeContainer getParent() { return this._parent; } - + protected PaintStyle getFillPaint() { final XSLFTheme theme = getSheet().getTheme(); final boolean hasPlaceholder = getPlaceholder() != null; PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() { + @Override public boolean fetch(XSLFShape shape) { XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(shape.getShapeProperties()); if (fp == null) { @@ -150,7 +153,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { setValue(null); return true; } - + PackagePart pp = shape.getSheet().getPackagePart(); PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder); if (paint != null) { @@ -167,8 +170,8 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { setValue(paint); return true; } - - + + return false; } }; @@ -181,16 +184,16 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { protected CTBackgroundProperties getBgPr() { return getChild(CTBackgroundProperties.class, PML_NS, "bgPr"); } - + @SuppressWarnings("unused") protected CTStyleMatrixReference getBgRef() { return getChild(CTStyleMatrixReference.class, PML_NS, "bgRef"); } - + protected CTGroupShapeProperties getGrpSpPr() { return getChild(CTGroupShapeProperties.class, PML_NS, "grpSpPr"); } - + protected CTNonVisualDrawingProps getCNvPr() { if (_nvPr == null) { String xquery = "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' .//*/p:cNvPr"; @@ -239,7 +242,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { public Placeholder getPlaceholder() { return getPlaceholderDetails().getPlaceholder(); } - + /** * @see PlaceholderDetails#setPlaceholder(Placeholder) */ @@ -268,7 +271,9 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { @SuppressWarnings({"unchecked", "WeakerAccess"}) protected <T extends XmlObject> T selectProperty(Class<T> resultClass, String xquery) { XmlObject[] rs = getXmlObject().selectPath(xquery); - if (rs.length == 0) return null; + if (rs.length == 0) { + return null; + } return (resultClass.isInstance(rs[0])) ? (T)rs[0] : null; } @@ -281,7 +286,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { * <li>slideLayout * <li>slideMaster * </ol> - * + * * Currently themes and their defaults aren't correctly handled * * @param visitor the object that collects the desired property @@ -299,7 +304,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { return false; } MasterSheet<XSLFShape,XSLFTextParagraph> sm = getSheet().getMasterSheet(); - + // try slide layout if (sm instanceof XSLFSlideLayout) { XSLFSlideLayout slideLayout = (XSLFSlideLayout)sm; @@ -309,7 +314,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { } sm = slideLayout.getMasterSheet(); } - + // try slide master if (sm instanceof XSLFSlideMaster) { XSLFSlideMaster master = (XSLFSlideMaster)sm; @@ -317,15 +322,15 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { XSLFSimpleShape masterShape = master.getPlaceholderByType(textType); return masterShape != null && visitor.fetch(masterShape); } - + return false; } - + private static int getPlaceholderType(CTPlaceholder ph) { if ( !ph.isSetType()) { return STPlaceholderType.INT_BODY; } - + switch (ph.getType().intValue()) { case STPlaceholderType.INT_TITLE: case STPlaceholderType.INT_CTR_TITLE: @@ -397,7 +402,8 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { throw new RuntimeException(e); } } - + + @Override public InputStream getImageData() { try { return getPart().getInputStream(); @@ -406,17 +412,19 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { } } + @Override public String getContentType() { /* TOOD: map content-type */ return getPart().getContentType(); } + @Override public int getAlpha() { return (blip.sizeOfAlphaModFixArray() > 0) ? blip.getAlphaModFixArray(0).getAmt() : 100000; } - }; + }; } @SuppressWarnings("WeakerAccess") @@ -426,14 +434,14 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { final CTGradientStop[] gs = gradFill.getGsLst().getGsArray(); Arrays.sort(gs, (o1, o2) -> { - Integer pos1 = o1.getPos(); - Integer pos2 = o2.getPos(); - return pos1.compareTo(pos2); + int pos1 = o1.getPos(); + int pos2 = o2.getPos(); + return Integer.compare(pos1, pos2); }); final ColorStyle cs[] = new ColorStyle[gs.length]; final float fractions[] = new float[gs.length]; - + int i=0; for (CTGradientStop cgs : gs) { CTSchemeColor phClrCgs = phClr; @@ -444,32 +452,37 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { fractions[i] = cgs.getPos() / 100000.f; i++; } - + return new GradientPaint() { + @Override public double getGradientAngle() { return (gradFill.isSetLin()) ? gradFill.getLin().getAng() / 60000.d : 0; } + @Override public ColorStyle[] getGradientColors() { return cs; } + @Override public float[] getGradientFractions() { return fractions; } + @Override public boolean isRotatedWithShape() { return gradFill.getRotWithShape(); } + @Override public GradientType getGradientType() { if (gradFill.isSetLin()) { return GradientType.linear; } - + if (gradFill.isSetPath()) { /* TODO: handle rect path */ STPathShadeType.Enum ps = gradFill.getPath().getPath(); @@ -479,16 +492,18 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { return GradientType.shape; } } - + return GradientType.linear; } - }; + }; } - + @SuppressWarnings("WeakerAccess") protected static PaintStyle selectPaint(CTStyleMatrixReference fillRef, final XSLFTheme theme, boolean isLineStyle, boolean hasPlaceholder) { - if (fillRef == null) return null; - + if (fillRef == null) { + return null; + } + // The idx attribute refers to the index of a fill style or // background fill style within the presentation's style matrix, defined by the fmtScheme element. // value of 0 or 1000 indicates no background, @@ -513,7 +528,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { fp = XSLFPropertiesDelegate.getFillDelegate(cur.getObject()); } cur.dispose(); - + CTSchemeColor phClr = fillRef.getSchemeClr(); PaintStyle res = selectPaint(fp, phClr, theme.getPackagePart(), theme, hasPlaceholder); // check for empty placeholder value @@ -524,12 +539,12 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { XSLFColor col = new XSLFColor(fillRef, theme, phClr); return DrawPaint.createSolidPaint(col.getColorStyle()); } - + @Override public void draw(Graphics2D graphics, Rectangle2D bounds) { DrawFactory.getInstance(graphics).drawShape(graphics, this, bounds); } - + /** * Return the shape specific (visual) properties * diff --git a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java index c4fbb4ca3b..2ae1293113 100644 --- a/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java +++ b/src/ooxml/java/org/apache/poi/xslf/util/PPTX2PNG.java @@ -74,13 +74,13 @@ public class PPTX2PNG { for (int i = 0; i < args.length; i++) { if (args[i].startsWith("-")) { if ("-scale".equals(args[i])) { - scale = Float.parseFloat(args[++i]); + scale = Float.parseFloat(args[++i]); // lgtm[java/index-out-of-bounds] } else if ("-slide".equals(args[i])) { - slidenumStr = args[++i]; + slidenumStr = args[++i]; // lgtm[java/index-out-of-bounds] } else if ("-format".equals(args[i])) { - format = args[++i]; + format = args[++i]; // lgtm[java/index-out-of-bounds] } else if ("-outdir".equals(args[i])) { - outdir = new File(args[++i]); + outdir = new File(args[++i]); // lgtm[java/index-out-of-bounds] } else if ("-quiet".equals(args[i])) { quiet = true; } @@ -98,11 +98,11 @@ public class PPTX2PNG { usage("Invalid format given"); return; } - + if (outdir == null) { outdir = file.getParentFile(); } - + if (!"null".equals(format) && (outdir == null || !outdir.exists() || !outdir.isDirectory())) { usage("Output directory doesn't exist"); return; @@ -112,7 +112,7 @@ public class PPTX2PNG { usage("Invalid scale given"); return; } - + if (!quiet) { System.out.println("Processing " + file); } @@ -168,7 +168,7 @@ public class PPTX2PNG { System.out.println("Done"); } } - + private static Set<Integer> slideIndexes(final int slideCount, String range) { Set<Integer> slideIdx = new TreeSet<>(); if ("-1".equals(range)) { diff --git a/src/ooxml/java/org/apache/poi/xssf/binary/XSSFBSheetHandler.java b/src/ooxml/java/org/apache/poi/xssf/binary/XSSFBSheetHandler.java index fb3f433bd6..726c2eeb89 100644 --- a/src/ooxml/java/org/apache/poi/xssf/binary/XSSFBSheetHandler.java +++ b/src/ooxml/java/org/apache/poi/xssf/binary/XSSFBSheetHandler.java @@ -302,9 +302,8 @@ public class XSSFBSheetHandler extends XSSFBParser { } private double rkNumber(byte[] data, int offset) { - //see 2.5.122 for this abomination + //see 2.5.122 byte b0 = data[offset]; - String s = Integer.toString(b0, 2); boolean numDivBy100 = ((b0 & 1) == 1); // else as is boolean floatingPoint = ((b0 >> 1 & 1) == 0); // else signed integer @@ -320,7 +319,8 @@ public class XSSFBSheetHandler extends XSSFBParser { if (floatingPoint) { d = LittleEndian.getDouble(rkBuffer); } else { - d = LittleEndian.getInt(rkBuffer); + int rawInt = LittleEndian.getInt(rkBuffer, 4); + d = rawInt >> 2;//divide by 4/shift bits coz 30 bit int, not 32 } d = (numDivBy100) ? d/100 : d; return d; diff --git a/src/ooxml/java/org/apache/poi/xssf/binary/XSSFBUtils.java b/src/ooxml/java/org/apache/poi/xssf/binary/XSSFBUtils.java index b2176d2d5b..6f1dd70bd3 100644 --- a/src/ooxml/java/org/apache/poi/xssf/binary/XSSFBUtils.java +++ b/src/ooxml/java/org/apache/poi/xssf/binary/XSSFBUtils.java @@ -51,7 +51,7 @@ public class XSSFBUtils { int numBytes = 2*(int)numChars; offset += 4; if (offset+numBytes > data.length) { - throw new XSSFBParseException("trying to read beyond data length:" + + throw new XSSFBParseException("trying to read beyond data length: " + "offset="+offset+", numBytes="+numBytes+", data.length="+data.length); } sb.append(new String(data, offset, numBytes, StandardCharsets.UTF_16LE)); diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java index 9884ce0935..9f0379493b 100644 --- a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFRow.java @@ -39,22 +39,22 @@ import org.apache.poi.util.NotImplemented; public class SXSSFRow implements Row, Comparable<SXSSFRow> { private static final Boolean UNDEFINED = null; - + private final SXSSFSheet _sheet; // parent sheet private final SortedMap<Integer, SXSSFCell> _cells = new TreeMap<>(); private short _style = -1; // index of cell style in style table private short _height = -1; // row height in twips (1/20 point) private boolean _zHeight; // row zero-height (this is somehow different than being hidden) private int _outlineLevel; // Outlining level of the row, when outlining is on - // use Boolean to have a tri-state for on/off/undefined + // use Boolean to have a tri-state for on/off/undefined private Boolean _hidden = UNDEFINED; private Boolean _collapsed = UNDEFINED; - + public SXSSFRow(SXSSFSheet sheet) { _sheet=sheet; } - + public Iterator<Cell> allCellsIterator() { return new CellIterator(); @@ -71,7 +71,7 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow> void setOutlineLevel(int level){ _outlineLevel = level; } - + /** * get row hidden state: Hidden (true), Unhidden (false), Undefined (null) * @@ -235,7 +235,7 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow> public SXSSFCell getCell(int cellnum, MissingCellPolicy policy) { checkBounds(cellnum); - + final SXSSFCell cell = _cells.get(cellnum); switch (policy) { case RETURN_NULL_AND_BLANK: @@ -345,10 +345,11 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow> @Override public void setHeightInPoints(float height) { - if(height==-1) + if(height==-1) { _height=-1; - else + } else { _height=(short)(height*20); + } } /** @@ -375,7 +376,7 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow> { return (float)(_height==-1?getSheet().getDefaultRowHeightInPoints():_height/20.0); } - + /** * Is this row formatted? Most aren't, but some rows * do have whole-row styles. For those that do, you @@ -392,16 +393,18 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow> */ @Override public CellStyle getRowStyle() { - if(!isFormatted()) return null; - + if(!isFormatted()) { + return null; + } + return getSheet().getWorkbook().getCellStyleAt(_style); } - + @Internal /*package*/ int getRowStyleIndex() { return _style; } - + /** * Applies a whole-row cell styling to the row. * The row style can be cleared by passing in <code>null</code>. @@ -440,7 +443,7 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow> /** * Create an iterator over the cells from [0, getLastCellNum()). * Includes blank cells, excludes empty cells - * + * * Returns an iterator over all filled cells (created via Row.createCell()) * Throws ConcurrentModificationException if cells are added, moved, or * removed after the iterator is created. @@ -485,10 +488,11 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow> @Override public Cell next() throws NoSuchElementException { - if (hasNext()) + if (hasNext()) { return _cells.get(pos++); - else + } else { throw new NoSuchElementException(); + } } @Override public void remove() @@ -496,7 +500,7 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow> throw new UnsupportedOperationException(); } } - + /** * Compares two <code>SXSSFRow</code> objects. Two rows are equal if they belong to the same worksheet and * their row indexes are equal. @@ -524,9 +528,9 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow> throw new IllegalArgumentException("The compared rows must belong to the same sheet"); } - Integer thisRow = this.getRowNum(); - Integer otherRow = other.getRowNum(); - return thisRow.compareTo(otherRow); + int thisRow = this.getRowNum(); + int otherRow = other.getRowNum(); + return Integer.compare(thisRow, otherRow); } @Override diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java index adb951d77a..bbad20e518 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java @@ -19,6 +19,7 @@ package org.apache.poi.xssf.usermodel; import org.apache.poi.ss.formula.BaseFormulaEvaluator; import org.apache.poi.ss.formula.EvaluationCell; +import org.apache.poi.ss.formula.EvaluationWorkbook; import org.apache.poi.ss.formula.WorkbookEvaluator; import org.apache.poi.ss.formula.eval.BoolEval; import org.apache.poi.ss.formula.eval.ErrorEval; @@ -26,6 +27,7 @@ import org.apache.poi.ss.formula.eval.NumberEval; import org.apache.poi.ss.formula.eval.StringEval; import org.apache.poi.ss.formula.eval.ValueEval; import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.CellValue; import org.apache.poi.ss.usermodel.RichTextString; @@ -69,4 +71,16 @@ public abstract class BaseXSSFFormulaEvaluator extends BaseFormulaEvaluator { } throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")"); } + + protected void setCellType(Cell cell, CellType cellType) { + if (cell instanceof XSSFCell) { + EvaluationWorkbook evaluationWorkbook = getEvaluationWorkbook(); + BaseXSSFEvaluationWorkbook xewb = BaseXSSFEvaluationWorkbook.class.isAssignableFrom(evaluationWorkbook.getClass()) ? (BaseXSSFEvaluationWorkbook) evaluationWorkbook : null; + + ((XSSFCell) cell).setCellType(cellType, xewb); + } else { + // could be an SXSSFCell + cell.setCellType(cellType); + } + } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java index b3113203a6..a9490694c0 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -37,6 +37,7 @@ import org.apache.poi.ss.usermodel.Comment; import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.FormulaError; +import org.apache.poi.ss.usermodel.FormulaEvaluator; import org.apache.poi.ss.usermodel.Hyperlink; import org.apache.poi.ss.usermodel.RichTextString; import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; @@ -477,7 +478,7 @@ public final class XSSFCell implements Cell { * @return a formula for the cell * @throws IllegalStateException if the cell type returned by {@link #getCellType()} is not {@link CellType#FORMULA} */ - protected String getCellFormula(XSSFEvaluationWorkbook fpb) { + protected String getCellFormula(BaseXSSFEvaluationWorkbook fpb) { CellType cellType = getCellType(); if(cellType != CellType.FORMULA) { throw typeMismatch(CellType.FORMULA, cellType, false); @@ -506,7 +507,7 @@ public final class XSSFCell implements Cell { * @param si Shared Group Index * @return non shared formula created for the given shared formula and this cell */ - private String convertSharedFormula(int si, XSSFEvaluationWorkbook fpb){ + private String convertSharedFormula(int si, BaseXSSFEvaluationWorkbook fpb){ XSSFSheet sheet = getSheet(); CTCellFormula f = sheet.getSharedFormula(si); @@ -536,6 +537,10 @@ public final class XSSFCell implements Cell { * Note, this method only sets the formula string and does not calculate the formula value. * To set the precalculated value use {@link #setCellValue(double)} or {@link #setCellValue(String)} * </p> + * <p> + * Note, if there are any shared formulas, his will invalidate any + * {@link FormulaEvaluator} instances based on this workbook. + * </p> * * @param formula the formula to set, e.g. <code>"SUM(C4:E4)"</code>. * If the argument is <code>null</code> then the current formula is removed. @@ -563,7 +568,7 @@ public final class XSSFCell implements Cell { if (formula == null) { wb.onDeleteFormula(this); if (_cell.isSetF()) { - _row.getSheet().onDeleteFormula(this); + _row.getSheet().onDeleteFormula(this, null); _cell.unsetF(); } return; @@ -962,6 +967,16 @@ public final class XSSFCell implements Cell { */ @Override public void setCellType(CellType cellType) { + setCellType(cellType, null); + } + + /** + * Needed by bug #62834, which points out getCellFormula() expects an evaluation context or creates a new one, + * so if there is one in use, it needs to be carried on through. + * @param cellType + * @param evalWb BaseXSSFEvaluationWorkbook already in use, or null if a new implicit one should be used + */ + protected void setCellType(CellType cellType, BaseXSSFEvaluationWorkbook evalWb) { CellType prevType = getCellType(); if(isPartOfArrayFormulaGroup()){ @@ -969,7 +984,7 @@ public final class XSSFCell implements Cell { } if(prevType == CellType.FORMULA && cellType != CellType.FORMULA) { if (_cell.isSetF()) { - _row.getSheet().onDeleteFormula(this); + _row.getSheet().onDeleteFormula(this, evalWb); } getSheet().getWorkbook().onDeleteFormula(this); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java index 3f42faab5f..1ae1e99217 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java @@ -446,7 +446,7 @@ public final class XSSFDrawing extends POIXMLDocumentPart implements Drawing<XSS XSSFSheet sheet = getSheet(); XSSFWorkbook wb = sheet.getWorkbook(); int sheetIndex = wb.getSheetIndex(sheet); - long shapeId = (sheetIndex + 1) * 1024 + newShapeId(); + long shapeId = (sheetIndex + 1L) * 1024 + newShapeId(); // add reference to OLE part PackagePartName olePN; diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java index 0217f83117..66307c206d 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java @@ -22,8 +22,8 @@ import java.util.Iterator; import java.util.Set; import java.util.TreeMap; -import org.apache.poi.ss.formula.FormulaShifter; import org.apache.poi.ss.SpreadsheetVersion; +import org.apache.poi.ss.formula.FormulaShifter; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellCopyPolicy; import org.apache.poi.ss.usermodel.CellStyle; @@ -77,7 +77,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { _cells.put(colI, cell); sheet.onReadCell(cell); } - + if (! row.isSetR()) { // Certain file format writers skip the row number // Assume no gaps, and give this the next row number @@ -158,9 +158,9 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { throw new IllegalArgumentException("The compared rows must belong to the same sheet"); } - Integer thisRow = this.getRowNum(); - Integer otherRow = other.getRowNum(); - return thisRow.compareTo(otherRow); + int thisRow = this.getRowNum(); + int otherRow = other.getRowNum(); + return Integer.compare(thisRow, otherRow); } @Override @@ -245,7 +245,9 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { */ @Override public XSSFCell getCell(int cellnum, MissingCellPolicy policy) { - if(cellnum < 0) throw new IllegalArgumentException("Cell index must be >= 0"); + if(cellnum < 0) { + throw new IllegalArgumentException("Cell index must be >= 0"); + } // Performance optimization for bug 57840: explicit boxing is slightly faster than auto-unboxing, though may use more memory final Integer colI = Integer.valueOf(cellnum); // NOSONAR @@ -332,8 +334,12 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { @Override public void setHeight(short height) { if (height == -1) { - if (_row.isSetHt()) _row.unsetHt(); - if (_row.isSetCustomHeight()) _row.unsetCustomHeight(); + if (_row.isSetHt()) { + _row.unsetHt(); + } + if (_row.isSetCustomHeight()) { + _row.unsetCustomHeight(); + } } else { _row.setHt((double) height / 20); _row.setCustomHeight(true); @@ -425,8 +431,10 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { */ @Override public XSSFCellStyle getRowStyle() { - if(!isFormatted()) return null; - + if(!isFormatted()) { + return null; + } + StylesTable stylesSource = getSheet().getWorkbook().getStylesSource(); if(stylesSource.getNumCellStyles() > 0) { return stylesSource.getStyleAt((int)_row.getS()); @@ -434,7 +442,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { return null; } } - + /** * Applies a whole-row cell styling to the row. * If the value is null then the style information is removed, @@ -449,7 +457,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { } } else { StylesTable styleSource = getSheet().getWorkbook().getStylesSource(); - + XSSFCellStyle xStyle = (XSSFCellStyle)style; xStyle.verifyBelongsToStylesSource(styleSource); @@ -458,7 +466,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { _row.setCustomFormat(true); } } - + /** * Remove the Cell from this row. * @@ -502,8 +510,8 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { int i = 0; for (XSSFCell xssfCell : _cells.values()) { cArray[i] = (CTCell) xssfCell.getCTCell().copy(); - - // we have to copy and re-create the XSSFCell here because the + + // we have to copy and re-create the XSSFCell here because the // elements as otherwise setCArray below invalidates all the columns! // see Bug 56170, XMLBeans seems to always release previous objects // in the CArray, so we need to provide completely new ones here! @@ -537,7 +545,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { } setRowNum(rownum); } - + /** * Copy the cells from srcRow to this row * If this row is not a blank row, this will merge the two rows, overwriting @@ -589,7 +597,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { final int srcRowNum = srcRow.getRowNum(); final int destRowNum = getRowNum(); final int rowDifference = destRowNum - srcRowNum; - + final FormulaShifter formulaShifter = FormulaShifter.createForRowCopy(sheetIndex, sheetName, srcRowNum, srcRowNum, rowDifference, SpreadsheetVersion.EXCEL2007); final XSSFRowShifter rowShifter = new XSSFRowShifter(_sheet); rowShifter.updateRowFormulas(this, formulaShifter); @@ -617,7 +625,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { public int getOutlineLevel() { return _row.getOutlineLevel(); } - + /** * Shifts column range [firstShiftColumnIndex-lastShiftColumnIndex] step places to the right. * @param firstShiftColumnIndex the column to start shifting @@ -626,20 +634,23 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { */ @Override public void shiftCellsRight(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) { - if(step < 0) + if(step < 0) { throw new IllegalArgumentException("Shifting step may not be negative "); - if(firstShiftColumnIndex > lastShiftColumnIndex) + } + if(firstShiftColumnIndex > lastShiftColumnIndex) { throw new IllegalArgumentException(String.format(LocaleUtil.getUserLocale(), "Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex)); - for (int columnIndex = lastShiftColumnIndex; columnIndex >= firstShiftColumnIndex; columnIndex--){ // process cells backwards, because of shifting + } + for (int columnIndex = lastShiftColumnIndex; columnIndex >= firstShiftColumnIndex; columnIndex--){ // process cells backwards, because of shifting shiftCell(columnIndex, step); } for (int columnIndex = firstShiftColumnIndex; columnIndex <= firstShiftColumnIndex+step-1; columnIndex++) { _cells.remove(columnIndex); XSSFCell targetCell = getCell(columnIndex); - if(targetCell != null) + if(targetCell != null) { targetCell.getCTCell().set(CTCell.Factory.newInstance()); + } } } /** @@ -650,27 +661,32 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { */ @Override public void shiftCellsLeft(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) { - if(step < 0) + if(step < 0) { throw new IllegalArgumentException("Shifting step may not be negative "); - if(firstShiftColumnIndex > lastShiftColumnIndex) + } + if(firstShiftColumnIndex > lastShiftColumnIndex) { throw new IllegalArgumentException(String.format(LocaleUtil.getUserLocale(), "Incorrect shifting range : %d-%d", firstShiftColumnIndex, lastShiftColumnIndex)); - if(firstShiftColumnIndex - step < 0) + } + if(firstShiftColumnIndex - step < 0) { throw new IllegalStateException("Column index less than zero : " + (Integer.valueOf(firstShiftColumnIndex + step)).toString()); - for (int columnIndex = firstShiftColumnIndex; columnIndex <= lastShiftColumnIndex; columnIndex++){ + } + for (int columnIndex = firstShiftColumnIndex; columnIndex <= lastShiftColumnIndex; columnIndex++){ shiftCell(columnIndex, -step); } for (int columnIndex = lastShiftColumnIndex-step+1; columnIndex <= lastShiftColumnIndex; columnIndex++){ _cells.remove(columnIndex); XSSFCell targetCell = getCell(columnIndex); - if(targetCell != null) + if(targetCell != null) { targetCell.getCTCell().set(CTCell.Factory.newInstance()); + } } } private void shiftCell(int columnIndex, int step/*pass negative value for left shift*/){ - if(columnIndex + step < 0) // only for shifting left + if(columnIndex + step < 0) { throw new IllegalStateException("Column index less than zero : " + (Integer.valueOf(columnIndex + step)).toString()); - + } + XSSFCell currentCell = getCell(columnIndex); if(currentCell != null){ currentCell.setCellNum(columnIndex+step); @@ -679,8 +695,9 @@ public class XSSFRow implements Row, Comparable<XSSFRow> { else { _cells.remove(columnIndex+step); XSSFCell targetCell = getCell(columnIndex+step); - if(targetCell != null) + if(targetCell != null) { targetCell.getCTCell().set(CTCell.Factory.newInstance()); + } } } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 423561b2ce..d776f8fc00 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -147,6 +147,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument; * contain text, numbers, dates, and formulas. Cells can also be formatted. * </p> */ +@SuppressWarnings("deprecation") public class XSSFSheet extends POIXMLDocumentPart implements Sheet { private static final POILogger logger = POILogFactory.getLogger(XSSFSheet.class); @@ -472,7 +473,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { /** * Verify that candidate region does not intersect with an existing merged region in this sheet * - * @param candidateRegion + * @param candidateRegion the range of cells to verify * @throws IllegalStateException if candidate region intersects an existing merged region in this sheet (or candidateRegion is already merged in this sheet) */ private void validateMergedRegions(CellRangeAddress candidateRegion) { @@ -855,8 +856,8 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { /** * Get a Hyperlink in this sheet anchored at row, column * - * @param row - * @param column + * @param row The row where the hyperlink is anchored + * @param column The column where the hyperlinkn is anchored * @return hyperlink if there is a hyperlink anchored at row, column; otherwise returns null */ @Override @@ -1480,8 +1481,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { * * @param startRowNum the first row number in this sheet to return * @param endRowNum the last row number in this sheet to return - * @param createRowIfMissing - * @return All rows between startRow and endRow, inclusive + * @param createRowIfMissing If missing rows should be created. + * @return All rows between startRow and endRow, inclusive. If createRowIfMissing is false, + * only previously existing rows are returned, otherwise empty rows are added as necessary * @throws IllegalArgumentException if startRowNum and endRowNum are not in ascending order */ private List<XSSFRow> getRows(int startRowNum, int endRowNum, boolean createRowIfMissing) { @@ -2472,7 +2474,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { * 'Collapsed' state is stored in a single column col info record * immediately after the outline group * - * @param idx + * @param idx The column-index to check * @return a boolean represented if the column is collapsed */ private boolean isColumnGroupCollapsed(int idx) { @@ -3052,7 +3054,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { rebuildRows(); } - private final void rebuildRows() { + private void rebuildRows() { //rebuild the _rows map List<XSSFRow> rowList = new ArrayList<>(_rows.values()); _rows.clear(); @@ -3114,25 +3116,22 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { // then do the actual moving and also adjust comments/rowHeight // we need to sort it in a way so the shifting does not mess up the structures, // i.e. when shifting down, start from down and go up, when shifting up, vice-versa - SortedMap<XSSFComment, Integer> commentsToShift = new TreeMap<>(new Comparator<XSSFComment>() { - @Override - public int compare(XSSFComment o1, XSSFComment o2) { - int row1 = o1.getRow(); - int row2 = o2.getRow(); - - if (row1 == row2) { - // ordering is not important when row is equal, but don't return zero to still - // get multiple comments per row into the map - return o1.hashCode() - o2.hashCode(); - } + SortedMap<XSSFComment, Integer> commentsToShift = new TreeMap<>((o1, o2) -> { + int row1 = o1.getRow(); + int row2 = o2.getRow(); + + if (row1 == row2) { + // ordering is not important when row is equal, but don't return zero to still + // get multiple comments per row into the map + return o1.hashCode() - o2.hashCode(); + } - // when shifting down, sort higher row-values first - if (n > 0) { - return row1 < row2 ? 1 : -1; - } else { - // sort lower-row values first when shifting up - return row1 > row2 ? 1 : -1; - } + // when shifting down, sort higher row-values first + if (n > 0) { + return row1 < row2 ? 1 : -1; + } else { + // sort lower-row values first when shifting up + return row1 > row2 ? 1 : -1; } }); @@ -3211,25 +3210,22 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { // then do the actual moving and also adjust comments/rowHeight // we need to sort it in a way so the shifting does not mess up the structures, // i.e. when shifting down, start from down and go up, when shifting up, vice-versa - SortedMap<XSSFComment, Integer> commentsToShift = new TreeMap<>(new Comparator<XSSFComment>() { - @Override - public int compare(XSSFComment o1, XSSFComment o2) { - int column1 = o1.getColumn(); - int column2 = o2.getColumn(); - - if (column1 == column2) { - // ordering is not important when row is equal, but don't return zero to still - // get multiple comments per row into the map - return o1.hashCode() - o2.hashCode(); - } + SortedMap<XSSFComment, Integer> commentsToShift = new TreeMap<>((o1, o2) -> { + int column1 = o1.getColumn(); + int column2 = o2.getColumn(); + + if (column1 == column2) { + // ordering is not important when row is equal, but don't return zero to still + // get multiple comments per row into the map + return o1.hashCode() - o2.hashCode(); + } - // when shifting down, sort higher row-values first - if (n > 0) { - return column1 < column2 ? 1 : -1; - } else { - // sort lower-row values first when shifting up - return column1 > column2 ? 1 : -1; - } + // when shifting down, sort higher row-values first + if (n > 0) { + return column1 < column2 ? 1 : -1; + } else { + // sort lower-row values first when shifting up + return column1 > column2 ? 1 : -1; } }); @@ -4096,7 +4092,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { * Creates a new Table, and associates it with this Sheet. * * @param tableArea - * the area that the table should cover, should not be {@null} + * the area that the table should cover, should not be null * @return the created table * @since 4.0.0 */ @@ -4421,18 +4417,15 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { + "defined source sheet " + sourceSheet.getSheetName() + "."); } - return createPivotTable(position, sourceSheet, new PivotTableReferenceConfigurator() { - @Override - public void configureReference(CTWorksheetSource wsSource) { - final String[] firstCell = source.getFirstCell().getCellRefParts(); - final String firstRow = firstCell[1]; - final String firstCol = firstCell[2]; - final String[] lastCell = source.getLastCell().getCellRefParts(); - final String lastRow = lastCell[1]; - final String lastCol = lastCell[2]; - final String ref = firstCol+firstRow+':'+lastCol+lastRow; //or just source.formatAsString() - wsSource.setRef(ref); - } + return createPivotTable(position, sourceSheet, wsSource -> { + final String[] firstCell = source.getFirstCell().getCellRefParts(); + final String firstRow = firstCell[1]; + final String firstCol = firstCell[2]; + final String[] lastCell = source.getLastCell().getCellRefParts(); + final String lastRow = lastCell[1]; + final String lastCol = lastCell[2]; + final String ref = firstCol+firstRow+':'+lastCol+lastRow; //or just source.formatAsString() + wsSource.setRef(ref); }); } @@ -4494,12 +4487,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { + "defined source sheet " + sourceSheet.getSheetName() + "."); } - return createPivotTable(position, sourceSheet, new PivotTableReferenceConfigurator() { - @Override - public void configureReference(CTWorksheetSource wsSource) { - wsSource.setName(source.getNameName()); - } - }); + return createPivotTable(position, sourceSheet, wsSource -> wsSource.setName(source.getNameName())); } /** @@ -4523,12 +4511,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { */ @Beta public XSSFPivotTable createPivotTable(final Table source, CellReference position) { - return createPivotTable(position, getWorkbook().getSheet(source.getSheetName()), new PivotTableReferenceConfigurator() { - @Override - public void configureReference(CTWorksheetSource wsSource) { - wsSource.setName(source.getName()); - } - }); + return createPivotTable(position, getWorkbook().getSheet(source.getSheetName()), wsSource -> wsSource.setName(source.getName())); } /** @@ -4621,8 +4604,10 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { /** * when a cell with a 'master' shared formula is removed, the next cell in the range becomes the master + * @param cell The cell that is removed + * @param evalWb BaseXSSFEvaluationWorkbook in use, if one exists */ - protected void onDeleteFormula(XSSFCell cell){ + protected void onDeleteFormula(XSSFCell cell, BaseXSSFEvaluationWorkbook evalWb){ CTCellFormula f = cell.getCTCell().getF(); if (f != null && f.getT() == STCellFormulaType.SHARED && f.isSetRef() && f.getStringValue() != null) { @@ -4634,9 +4619,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { XSSFRow row = getRow(i); if(row != null) for(int j = cell.getColumnIndex(); j <= ref.getLastColumn(); j++){ XSSFCell nextCell = row.getCell(j); - if(nextCell != null && nextCell != cell){ + if(nextCell != null && nextCell != cell && nextCell.getCellType() == CellType.FORMULA){ CTCellFormula nextF = nextCell.getCTCell().getF(); - nextF.setStringValue(nextCell.getCellFormula()); + nextF.setStringValue(nextCell.getCellFormula(evalWb)); CellRangeAddress nextRef = new CellRangeAddress( nextCell.getRowIndex(), ref.getLastRow(), nextCell.getColumnIndex(), ref.getLastColumn()); diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookFactory.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookFactory.java index 482654e8f9..7f72c163eb 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookFactory.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbookFactory.java @@ -29,6 +29,14 @@ import org.apache.poi.openxml4j.opc.ZipPackage; import org.apache.poi.ss.usermodel.WorkbookFactory; public class XSSFWorkbookFactory extends WorkbookFactory { + /** + * Create a new empty Workbook + * + * @return The created workbook + */ + public static XSSFWorkbook createWorkbook() { + return new XSSFWorkbook(); + } /** * Creates a XSSFWorkbook from the given OOXML Package. @@ -42,7 +50,6 @@ public class XSSFWorkbookFactory extends WorkbookFactory { * @return The created Workbook * * @throws IOException if an error occurs while reading the data - * @throws InvalidFormatException */ public static XSSFWorkbook create(OPCPackage pkg) throws IOException { return createWorkbook(pkg); @@ -59,7 +66,6 @@ public class XSSFWorkbookFactory extends WorkbookFactory { * @return The created Workbook * * @throws IOException if an error occurs while reading the data - * @throws InvalidFormatException */ public static XSSFWorkbook createWorkbook(ZipPackage pkg) throws IOException { return createWorkbook((OPCPackage)pkg); @@ -76,7 +82,6 @@ public class XSSFWorkbookFactory extends WorkbookFactory { * @return The created Workbook * * @throws IOException if an error occurs while reading the data - * @throws InvalidFormatException */ public static XSSFWorkbook createWorkbook(OPCPackage pkg) throws IOException { try { @@ -122,13 +127,11 @@ public class XSSFWorkbookFactory extends WorkbookFactory { * @return The created Workbook * * @throws IOException if an error occurs while reading the data - * @throws InvalidFormatException + * @throws InvalidFormatException if the package is not valid. */ @SuppressWarnings("resource") public static XSSFWorkbook createWorkbook(InputStream stream) throws IOException, InvalidFormatException { OPCPackage pkg = OPCPackage.open(stream); return createWorkbook(pkg); } - - } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java index 0aa7720867..f65856ea69 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/ColumnHelper.java @@ -50,7 +50,7 @@ public class ColumnHelper { TreeSet<CTCol> trackedCols = new TreeSet<>(CTColComparator.BY_MIN_MAX); CTCols newCols = CTCols.Factory.newInstance(); CTCols[] colsArray = worksheet.getColsArray(); - int i = 0; + int i; for (i = 0; i < colsArray.length; i++) { CTCols cols = colsArray[i]; for (CTCol col : cols.getColList()) { @@ -61,7 +61,7 @@ public class ColumnHelper { worksheet.removeCols(y); } - newCols.setColArray(trackedCols.toArray(new CTCol[trackedCols.size()])); + newCols.setColArray(trackedCols.toArray(new CTCol[0])); worksheet.addNewCols(); worksheet.setColsArray(0, newCols); } diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java index 587674edc3..f39c5cf482 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java @@ -40,21 +40,27 @@ public class XWPFSDTContent implements ISDTContent { // private final IBody part; // private final XWPFDocument document; - private List<XWPFParagraph> paragraphs = new ArrayList<>(); - private List<XWPFTable> tables = new ArrayList<>(); - private List<XWPFRun> runs = new ArrayList<>(); - private List<XWPFSDT> contentControls = new ArrayList<>(); + // private List<XWPFParagraph> paragraphs = new ArrayList<>(); + // private List<XWPFTable> tables = new ArrayList<>(); + // private List<XWPFRun> runs = new ArrayList<>(); + // private List<XWPFSDT> contentControls = new ArrayList<>(); private List<ISDTContents> bodyElements = new ArrayList<>(); public XWPFSDTContent(CTSdtContentRun sdtRun, IBody part, IRunBody parent) { + if (sdtRun == null) { + return; + } for (CTR ctr : sdtRun.getRArray()) { XWPFRun run = new XWPFRun(ctr, parent); - runs.add(run); + // runs.add(run); bodyElements.add(run); } } public XWPFSDTContent(CTSdtContentBlock block, IBody part, IRunBody parent) { + if (block == null) { + return; + } XmlCursor cursor = block.newCursor(); cursor.selectPath("./*"); while (cursor.toNextSelection()) { @@ -62,24 +68,25 @@ public class XWPFSDTContent implements ISDTContent { if (o instanceof CTP) { XWPFParagraph p = new XWPFParagraph((CTP) o, part); bodyElements.add(p); - paragraphs.add(p); + // paragraphs.add(p); } else if (o instanceof CTTbl) { XWPFTable t = new XWPFTable((CTTbl) o, part); bodyElements.add(t); - tables.add(t); + // tables.add(t); } else if (o instanceof CTSdtBlock) { XWPFSDT c = new XWPFSDT(((CTSdtBlock) o), part); bodyElements.add(c); - contentControls.add(c); + // contentControls.add(c); } else if (o instanceof CTR) { XWPFRun run = new XWPFRun((CTR) o, parent); - runs.add(run); + // runs.add(run); bodyElements.add(run); } } cursor.dispose(); } + @Override public String getText() { StringBuilder text = new StringBuilder(); boolean addNewLine = false; @@ -130,6 +137,7 @@ public class XWPFSDTContent implements ISDTContent { } } + @Override public String toString() { return getText(); } diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java index 0caca8c6dd..9352fe8a1d 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFTable.java @@ -161,8 +161,9 @@ public class XWPFTable implements IBodyElement, ISDTContents { this.ctTbl = table; // is an empty table: I add one row and one column as default - if (table.sizeOfTrArray() == 0) + if (table.sizeOfTrArray() == 0) { createEmptyTable(table); + } for (CTRow row : table.getTrList()) { StringBuilder rowText = new StringBuilder(); @@ -233,7 +234,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { return text.toString(); } - + /** * This method has existed since 2008 without an implementation. * It will be removed unless an implementation is provided. @@ -288,7 +289,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get the width value as an integer. * <p>If the width type is AUTO, DXA, or NIL, the value is 20ths of a point. If - * the width type is PCT, the value is the percentage times 50 (e.g., 2500 for 50%).</p> + * the width type is PCT, the value is the percentage times 50 (e.g., 2500 for 50%).</p> * @return width value as an integer */ public int getWidth() { @@ -322,7 +323,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { } /** - * Returns CTTblPr object for table. If force parameter is true, will + * Returns CTTblPr object for table. If force parameter is true, will * create the element if necessary. If force parameter is false, returns * null when CTTblPr element is missing. * @@ -343,7 +344,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { private CTTblBorders getTblBorders(boolean force) { CTTblPr tblPr = getTblPr(force); return tblPr == null ? null - : tblPr.isSetTblBorders() ? tblPr.getTblBorders() + : tblPr.isSetTblBorders() ? tblPr.getTblBorders() : force ? tblPr.addNewTblBorders() : null; } @@ -413,18 +414,18 @@ public class XWPFTable implements IBodyElement, ISDTContents { : tPr.isSetJc() ? TableRowAlign.valueOf(tPr.getJc().getVal().intValue()) : null; } - + /** * Set table alignment to specified {@link TableRowAlign} * - * @param ha {@link TableRowAlign} to set + * @param tra {@link TableRowAlign} to set */ public void setTableAlignment(TableRowAlign tra) { CTTblPr tPr = getTblPr(true); CTJc jc = tPr.isSetJc() ? tPr.getJc() : tPr.addNewJc(); jc.setVal(STJc.Enum.forInt(tra.getValue())); } - + /** * Removes the table alignment attribute from a table */ @@ -434,7 +435,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { tPr.unsetJc(); } } - + private void addColumn(XWPFTableRow tabRow, int sizeCol) { if (sizeCol > 0) { for (int i = 0; i < sizeCol; i++) { @@ -486,7 +487,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get inside horizontal border size - * + * * @return The width of the Inside Horizontal borders in 1/8th points, * -1 if missing. */ @@ -496,7 +497,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get inside horizontal border spacing - * + * * @return The offset to the Inside Horizontal borders in points, * -1 if missing. */ @@ -506,7 +507,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get inside horizontal border color - * + * * @return The color of the Inside Horizontal borders, null if missing. */ public String getInsideHBorderColor() { @@ -524,7 +525,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get inside vertical border size - * + * * @return The width of the Inside vertical borders in 1/8th points, * -1 if missing. */ @@ -534,7 +535,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get inside vertical border spacing - * + * * @return The offset to the Inside vertical borders in points, * -1 if missing. */ @@ -544,7 +545,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get inside vertical border color - * + * * @return The color of the Inside vertical borders, null if missing. */ public String getInsideVBorderColor() { @@ -562,7 +563,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get top border size - * + * * @return The width of the top borders in 1/8th points, * -1 if missing. */ @@ -572,7 +573,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get top border spacing - * + * * @return The offset to the top borders in points, * -1 if missing. */ @@ -582,7 +583,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get top border color - * + * * @return The color of the top borders, null if missing. */ public String getTopBorderColor() { @@ -600,7 +601,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get bottom border size - * + * * @return The width of the bottom borders in 1/8th points, * -1 if missing. */ @@ -610,7 +611,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get bottom border spacing - * + * * @return The offset to the bottom borders in points, * -1 if missing. */ @@ -620,7 +621,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get bottom border color - * + * * @return The color of the bottom borders, null if missing. */ public String getBottomBorderColor() { @@ -638,7 +639,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get Left border size - * + * * @return The width of the Left borders in 1/8th points, * -1 if missing. */ @@ -648,7 +649,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get Left border spacing - * + * * @return The offset to the Left borders in points, * -1 if missing. */ @@ -658,7 +659,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get Left border color - * + * * @return The color of the Left borders, null if missing. */ public String getLeftBorderColor() { @@ -676,7 +677,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get Right border size - * + * * @return The width of the Right borders in 1/8th points, * -1 if missing. */ @@ -686,7 +687,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get Right border spacing - * + * * @return The offset to the Right borders in points, * -1 if missing. */ @@ -696,7 +697,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { /** * Get Right border color - * + * * @return The color of the Right borders, null if missing. */ public String getRightBorderColor() { @@ -770,7 +771,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { * of a point) and a maximum value of 96 (twelve points). Any values outside this * range may be reassigned to a more appropriate value. * @param space - Specifies the spacing offset that shall be used to place this border on the table - * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), + * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), * or auto to allow a consumer to automatically determine the border color as appropriate. */ public void setInsideHBorder(XWPFBorderType type, int size, int space, String rgbColor) { @@ -786,7 +787,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { * of a point) and a maximum value of 96 (twelve points). Any values outside this * range may be reassigned to a more appropriate value. * @param space - Specifies the spacing offset that shall be used to place this border on the table - * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), + * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), * or auto to allow a consumer to automatically determine the border color as appropriate. */ public void setInsideVBorder(XWPFBorderType type, int size, int space, String rgbColor) { @@ -802,7 +803,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { * of a point) and a maximum value of 96 (twelve points). Any values outside this * range may be reassigned to a more appropriate value. * @param space - Specifies the spacing offset that shall be used to place this border on the table - * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), + * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), * or auto to allow a consumer to automatically determine the border color as appropriate. */ public void setTopBorder(XWPFBorderType type, int size, int space, String rgbColor) { @@ -818,7 +819,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { * of a point) and a maximum value of 96 (twelve points). Any values outside this * range may be reassigned to a more appropriate value. * @param space - Specifies the spacing offset that shall be used to place this border on the table - * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), + * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), * or auto to allow a consumer to automatically determine the border color as appropriate. */ public void setBottomBorder(XWPFBorderType type, int size, int space, String rgbColor) { @@ -834,7 +835,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { * of a point) and a maximum value of 96 (twelve points). Any values outside this * range may be reassigned to a more appropriate value. * @param space - Specifies the spacing offset that shall be used to place this border on the table - * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), + * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), * or auto to allow a consumer to automatically determine the border color as appropriate. */ public void setLeftBorder(XWPFBorderType type, int size, int space, String rgbColor) { @@ -850,7 +851,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { * of a point) and a maximum value of 96 (twelve points). Any values outside this * range may be reassigned to a more appropriate value. * @param space - Specifies the spacing offset that shall be used to place this border on the table - * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), + * @param rgbColor - This color may either be presented as a hex value (in RRGGBB format), * or auto to allow a consumer to automatically determine the border color as appropriate. */ public void setRightBorder(XWPFBorderType type, int size, int space, String rgbColor) { @@ -872,14 +873,14 @@ public class XWPFTable implements IBodyElement, ISDTContents { public void removeInsideHBorder() { removeBorder(Border.INSIDE_H); } - + /** * Remove inside vertical borders for table */ public void removeInsideVBorder() { removeBorder(Border.INSIDE_V); } - + /** * Remove top borders for table */ @@ -893,21 +894,21 @@ public class XWPFTable implements IBodyElement, ISDTContents { public void removeBottomBorder() { removeBorder(Border.BOTTOM); } - + /** * Remove left borders for table */ public void removeLeftBorder() { removeBorder(Border.LEFT); } - + /** * Remove right borders for table */ public void removeRightBorder() { removeBorder(Border.RIGHT); } - + /** * Remove all borders from table */ @@ -959,7 +960,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { } /** - * removes the Borders node from Table properties if there are + * removes the Borders node from Table properties if there are * no border elements */ private void cleanupTblBorders() { @@ -976,7 +977,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { } } } - + public int getCellMarginTop() { return getCellMargin(CTTblCellMar::getTop); } @@ -1095,10 +1096,12 @@ public class XWPFTable implements IBodyElement, ISDTContents { * * @see org.apache.poi.xwpf.usermodel.IBodyElement#getElementType() */ + @Override public BodyElementType getElementType() { return BodyElementType.TABLE; } + @Override public IBody getBody() { return part; } @@ -1108,6 +1111,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { * * @see org.apache.poi.xwpf.usermodel.IBody#getPart() */ + @Override public POIXMLDocumentPart getPart() { if (part != null) { return part.getPart(); @@ -1120,6 +1124,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { * * @see org.apache.poi.xwpf.usermodel.IBody#getPartType() */ + @Override public BodyType getPartType() { return part.getPartType(); } @@ -1130,11 +1135,13 @@ public class XWPFTable implements IBodyElement, ISDTContents { */ public XWPFTableRow getRow(CTRow row) { for (int i = 0; i < getRows().size(); i++) { - if (getRows().get(i).getCtRow() == row) return getRow(i); + if (getRows().get(i).getCtRow() == row) { + return getRow(i); + } } return null; } - + /** * Get the table width as a decimal value. * <p>If the width type is DXA or AUTO, then the value will always have @@ -1159,14 +1166,14 @@ public class XWPFTable implements IBodyElement, ISDTContents { protected static double getWidthDecimal(CTTblWidth ctWidth) { double result = 0.0; STTblWidth.Enum typeValue = ctWidth.getType(); - if (typeValue == STTblWidth.DXA - || typeValue == STTblWidth.AUTO + if (typeValue == STTblWidth.DXA + || typeValue == STTblWidth.AUTO || typeValue == STTblWidth.NIL) { result = 0.0 + ctWidth.getW().intValue(); } else if (typeValue == STTblWidth.PCT) { // Percentage values are stored as integers that are 50 times // percentage. - result = ctWidth.getW().intValue() / 50.0; + result = ctWidth.getW().intValue() / 50.0; } else { // Should never get here } @@ -1230,7 +1237,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { protected static void setWidthValue(String widthValue, CTTblWidth ctWidth) { if (!widthValue.matches(REGEX_WIDTH_VALUE)) { throw new RuntimeException("Table width value \"" + widthValue + "\" " - + "must match regular expression \"" + REGEX_WIDTH_VALUE + "\"."); + + "must match regular expression \"" + REGEX_WIDTH_VALUE + "\"."); } if (widthValue.matches("auto")) { ctWidth.setType(STTblWidth.AUTO); @@ -1240,13 +1247,13 @@ public class XWPFTable implements IBodyElement, ISDTContents { } else { // Must be an integer ctWidth.setW(new BigInteger(widthValue)); - ctWidth.setType(STTblWidth.DXA); + ctWidth.setType(STTblWidth.DXA); } } /** * Set the underlying table width value to a percentage value. - * @param ctWidth The CTTblWidth to set the value on + * @param ctWidth The CTTblWidth to set the value on * @param widthValue String width value in form "33.3%" or an integer that is 50 times desired percentage value (e.g, * 2500 for 50%) * @since 4.0.0 @@ -1257,7 +1264,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { String numberPart = widthValue.substring(0, widthValue.length() - 1); double percentage = Double.parseDouble(numberPart) * 50; long intValue = Math.round(percentage); - ctWidth.setW(BigInteger.valueOf(intValue)); + ctWidth.setW(BigInteger.valueOf(intValue)); } else if (widthValue.matches("[0-9]+")) { ctWidth.setW(new BigInteger(widthValue)); } else { @@ -1275,7 +1282,7 @@ public class XWPFTable implements IBodyElement, ISDTContents { * @since 4.0.0 */ public void setWidthType(TableWidthType widthType) { - setWidthType(widthType, getTblPr().getTblW()); + setWidthType(widthType, getTblPr().getTblW()); } /** diff --git a/src/ooxml/testcases/org/apache/poi/ooxml/util/OOXMLLite.java b/src/ooxml/testcases/org/apache/poi/ooxml/util/OOXMLLite.java index 4fe57d5346..450f958ae6 100644 --- a/src/ooxml/testcases/org/apache/poi/ooxml/util/OOXMLLite.java +++ b/src/ooxml/testcases/org/apache/poi/ooxml/util/OOXMLLite.java @@ -17,18 +17,6 @@ package org.apache.poi.ooxml.util; -import junit.framework.TestCase; -import org.apache.poi.util.IOUtils; -import org.apache.poi.util.StringUtil; -import org.apache.poi.util.SuppressForbidden; -import org.apache.xmlbeans.StringEnumAbstractBase; -import org.junit.Test; -import org.junit.internal.TextListener; -import org.junit.runner.Description; -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; -import org.reflections.Reflections; - import java.io.File; import java.io.IOException; import java.lang.reflect.Field; @@ -38,11 +26,29 @@ import java.security.AccessController; import java.security.CodeSource; import java.security.PrivilegedAction; import java.security.ProtectionDomain; -import java.util.*; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Vector; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.regex.Pattern; +import org.apache.poi.util.IOUtils; +import org.apache.poi.util.StringUtil; +import org.apache.poi.util.SuppressForbidden; +import org.apache.xmlbeans.StringEnumAbstractBase; +import org.junit.Test; +import org.junit.internal.TextListener; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.reflections.Reflections; + +import junit.framework.TestCase; + /** * Build a 'lite' version of the ooxml-schemas.jar * @@ -74,12 +80,12 @@ public final class OOXMLLite { } public static void main(String[] args) throws IOException { - System.out.println("Free memory (bytes): " + + System.out.println("Free memory (bytes): " + Runtime.getRuntime().freeMemory()); long maxMemory = Runtime.getRuntime().maxMemory(); - System.out.println("Maximum memory (bytes): " + + System.out.println("Maximum memory (bytes): " + (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory)); - System.out.println("Total memory (bytes): " + + System.out.println("Total memory (bytes): " + Runtime.getRuntime().totalMemory()); String dest = null, test = null, ooxml = null; @@ -87,13 +93,13 @@ public final class OOXMLLite { for (int i = 0; i < args.length; i++) { switch (args[i]) { case "-dest": - dest = args[++i]; + dest = args[++i]; // lgtm[java/index-out-of-bounds] break; case "-test": - test = args[++i]; + test = args[++i]; // lgtm[java/index-out-of-bounds] break; case "-ooxml": - ooxml = args[++i]; + ooxml = args[++i]; // lgtm[java/index-out-of-bounds] break; } } @@ -248,7 +254,7 @@ public final class OOXMLLite { return true; } } - + // also check super classes if(testclass.getSuperclass() != null) { for (Method m : testclass.getSuperclass().getDeclaredMethods()) { @@ -257,7 +263,7 @@ public final class OOXMLLite { } } } - + System.out.println("Class " + testclass.getName() + " does not derive from TestCase and does not have a @Test annotation"); // Should we also look at superclasses to find cases @@ -286,8 +292,12 @@ public final class OOXMLLite { String path = arg.getAbsolutePath(); String prefix = root.getAbsolutePath(); String cls = path.substring(prefix.length() + 1).replace(File.separator, "."); - if(!cls.matches(ptrn)) return; - if (cls.matches(exclude)) return; + if(!cls.matches(ptrn)) { + return; + } + if (cls.matches(exclude)) { + return; + } //ignore inner classes defined in tests if (cls.indexOf('$') != -1) { System.out.println("Inner class " + cls + " not included"); @@ -315,10 +325,11 @@ public final class OOXMLLite { */ @SuppressWarnings("unchecked") private static Set<Class<?>> getLoadedClasses(String ptrn) { - // make the field accessible, we defer this from static initialization to here to + // make the field accessible, we defer this from static initialization to here to // allow JDKs which do not have this field (e.g. IBM JDK) to at least load the class // without failing, see https://issues.apache.org/bugzilla/show_bug.cgi?id=56550 final Field _classes = AccessController.doPrivileged(new PrivilegedAction<Field>() { + @Override @SuppressForbidden("TODO: Reflection works until Java 8 on Oracle/Sun JDKs, but breaks afterwards (different classloader types, access checks)") public Field run() { try { @@ -339,11 +350,17 @@ public final class OOXMLLite { for (Class<?> cls : classes) { // e.g. proxy-classes, ... ProtectionDomain pd = cls.getProtectionDomain(); - if (pd == null) continue; + if (pd == null) { + continue; + } CodeSource cs = pd.getCodeSource(); - if (cs == null) continue; + if (cs == null) { + continue; + } URL loc = cs.getLocation(); - if (loc == null) continue; + if (loc == null) { + continue; + } String jar = loc.toString(); if (jar.contains(ptrn)) { diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/ZipFileAssert.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/ZipFileAssert.java index f14052172f..05325791ff 100644 --- a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/ZipFileAssert.java +++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/ZipFileAssert.java @@ -30,7 +30,6 @@ import java.io.IOException; import java.util.Set; import java.util.TreeMap; -import junit.framework.AssertionFailedError; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import org.apache.poi.util.IOUtils; @@ -44,125 +43,130 @@ import org.xmlunit.diff.Diff; import org.xmlunit.diff.DifferenceEvaluator; import org.xmlunit.diff.ElementSelectors; +import junit.framework.AssertionFailedError; + /** * Compare the contents of 2 zip files. */ public final class ZipFileAssert { - private ZipFileAssert() { - } - - private static void equals( - TreeMap<String, ByteArrayOutputStream> file1, - TreeMap<String, ByteArrayOutputStream> file2) { - Set<String> listFile1 = file1.keySet(); - Assert.assertEquals("not the same number of files in zip:", listFile1.size(), file2.keySet().size()); - - for (String fileName : listFile1) { - // extract the contents for both - ByteArrayOutputStream contain1 = file1.get(fileName); - ByteArrayOutputStream contain2 = file2.get(fileName); - - assertNotNull(fileName + " not found in 2nd zip", contain2); - // no need to check for contain1. The key come from it - - if (fileName.matches(".*\\.(xml|rels)$")) { - // we have a xml file - final Diff diff = DiffBuilder. - compare(Input.fromByteArray(contain1.toByteArray())). - withTest(Input.fromByteArray(contain2.toByteArray())). - ignoreWhitespace(). - checkForSimilar(). - withDifferenceEvaluator(new IgnoreXMLDeclEvaluator()). - withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndAllAttributes, ElementSelectors.byNameAndText)). - build(); - assertFalse(fileName+": "+diff.toString(), diff.hasDifferences()); + private ZipFileAssert() { + } + + private static void equals( + TreeMap<String, ByteArrayOutputStream> file1, + TreeMap<String, ByteArrayOutputStream> file2) { + Set<String> listFile1 = file1.keySet(); + Assert.assertEquals("not the same number of files in zip:", listFile1.size(), file2.keySet().size()); + + for (String fileName : listFile1) { + // extract the contents for both + ByteArrayOutputStream contain1 = file1.get(fileName); + ByteArrayOutputStream contain2 = file2.get(fileName); + + assertNotNull(fileName + " not found in 2nd zip", contain2); + // no need to check for contain1. The key come from it + + if (fileName.matches(".*\\.(xml|rels)$")) { + // we have a xml file + final Diff diff = DiffBuilder. + compare(Input.fromByteArray(contain1.toByteArray())). + withTest(Input.fromByteArray(contain2.toByteArray())). + ignoreWhitespace(). + checkForSimilar(). + withDifferenceEvaluator(new IgnoreXMLDeclEvaluator()). + withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.byNameAndAllAttributes, ElementSelectors.byNameAndText)). + build(); + assertFalse(fileName+": "+diff.toString(), diff.hasDifferences()); } else { - // not xml, may be an image or other binary format + // not xml, may be an image or other binary format Assert.assertEquals(fileName + " does not have the same size in both zip:", contain1.size(), contain2.size()); - assertArrayEquals("contents differ", contain1.toByteArray(), contain2.toByteArray()); - } - } - } - - private static TreeMap<String, ByteArrayOutputStream> decompress( - File filename) throws IOException { - // store the zip content in memory - // let s assume it is not Go ;-) - TreeMap<String, ByteArrayOutputStream> zipContent = new TreeMap<>(); - - /* Open file to decompress */ - FileInputStream file_decompress = new FileInputStream(filename); - - /* Create a buffer for the decompressed files */ - BufferedInputStream buffi = new BufferedInputStream(file_decompress); - - /* Open the file with the buffer */ - ZipArchiveInputStream zis = new ZipArchiveInputStream(buffi); - - /* Processing entries of the zip file */ - ArchiveEntry entree; - while ((entree = zis.getNextEntry()) != null) { - - /* Create a array for the current entry */ - ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); - IOUtils.copy(zis, byteArray); - zipContent.put(entree.getName(), byteArray); - } - - zis.close(); - - return zipContent; - } - - /** - * Asserts that two files are equal. Throws an <tt>AssertionFailedError</tt> - * if they are not. - * <p> - * - */ - public static void assertEquals(File expected, File actual) { - assertNotNull(expected); - assertNotNull(actual); - - assertTrue("File does not exist [" + expected.getAbsolutePath() - + "]", expected.exists()); - assertTrue("File does not exist [" + actual.getAbsolutePath() - + "]", actual.exists()); - - assertTrue("Expected file not readable", expected.canRead()); - assertTrue("Actual file not readable", actual.canRead()); - - try { - TreeMap<String, ByteArrayOutputStream> file1 = decompress(expected); - TreeMap<String, ByteArrayOutputStream> file2 = decompress(actual); - equals(file1, file2); - } catch (IOException e) { - throw new AssertionFailedError(e.toString()); - } - } - - private static class IgnoreXMLDeclEvaluator implements DifferenceEvaluator { - public ComparisonResult evaluate(final Comparison comparison, final ComparisonResult outcome) { - if (outcome != ComparisonResult.EQUAL) { - // only evaluate differences - switch (comparison.getType()) { - case CHILD_NODELIST_SEQUENCE: - case XML_STANDALONE: - case NAMESPACE_PREFIX: - return ComparisonResult.SIMILAR; - case TEXT_VALUE: - switch (comparison.getControlDetails().getTarget().getParentNode().getNodeName()) { - case "dcterms:created": - case "dc:creator": - return ComparisonResult.SIMILAR; - } - break; - default: - break; - } - } - - return outcome; - } - } + assertArrayEquals("contents differ", contain1.toByteArray(), contain2.toByteArray()); + } + } + } + + private static TreeMap<String, ByteArrayOutputStream> decompress( + File filename) throws IOException { + // store the zip content in memory + // let s assume it is not Go ;-) + TreeMap<String, ByteArrayOutputStream> zipContent = new TreeMap<>(); + + try ( + /* Open file to decompress */ + FileInputStream file_decompress = new FileInputStream(filename); + + /* Create a buffer for the decompressed files */ + BufferedInputStream buffi = new BufferedInputStream(file_decompress); + + /* Open the file with the buffer */ + ZipArchiveInputStream zis = new ZipArchiveInputStream(buffi); + ) { + + /* Processing entries of the zip file */ + ArchiveEntry entree; + while ((entree = zis.getNextEntry()) != null) { + + /* Create a array for the current entry */ + ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); + IOUtils.copy(zis, byteArray); + zipContent.put(entree.getName(), byteArray); + } + + } + + return zipContent; + } + + /** + * Asserts that two files are equal. Throws an <tt>AssertionFailedError</tt> + * if they are not. + * <p> + * + */ + public static void assertEquals(File expected, File actual) { + assertNotNull(expected); + assertNotNull(actual); + + assertTrue("File does not exist [" + expected.getAbsolutePath() + + "]", expected.exists()); + assertTrue("File does not exist [" + actual.getAbsolutePath() + + "]", actual.exists()); + + assertTrue("Expected file not readable", expected.canRead()); + assertTrue("Actual file not readable", actual.canRead()); + + try { + TreeMap<String, ByteArrayOutputStream> file1 = decompress(expected); + TreeMap<String, ByteArrayOutputStream> file2 = decompress(actual); + equals(file1, file2); + } catch (IOException e) { + throw new AssertionFailedError(e.toString()); + } + } + + private static class IgnoreXMLDeclEvaluator implements DifferenceEvaluator { + @Override + public ComparisonResult evaluate(final Comparison comparison, final ComparisonResult outcome) { + if (outcome != ComparisonResult.EQUAL) { + // only evaluate differences + switch (comparison.getType()) { + case CHILD_NODELIST_SEQUENCE: + case XML_STANDALONE: + case NAMESPACE_PREFIX: + return ComparisonResult.SIMILAR; + case TEXT_VALUE: + switch (comparison.getControlDetails().getTarget().getParentNode().getNodeName()) { + case "dcterms:created": + case "dc:creator": + return ComparisonResult.SIMILAR; + } + break; + default: + break; + } + } + + return outcome; + } + } } diff --git a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/TestOPCComplianceCoreProperties.java b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/TestOPCComplianceCoreProperties.java index d663bb4cd3..542f1c0131 100644 --- a/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/TestOPCComplianceCoreProperties.java +++ b/src/ooxml/testcases/org/apache/poi/openxml4j/opc/compliance/TestOPCComplianceCoreProperties.java @@ -83,9 +83,7 @@ public final class TestOPCComplianceCoreProperties { try { InputStream is = OpenXML4JTestDataSamples.openComplianceSampleStream("OPCCompliance_CoreProperties_OnlyOneCorePropertiesPart.docx"); pkg = OPCPackage.open(is); - } catch (InvalidFormatException e) { - throw new RuntimeException(e); - } catch (IOException e) { + } catch (InvalidFormatException | IOException e) { throw new RuntimeException(e); } pkg.revert(); @@ -151,9 +149,7 @@ public final class TestOPCComplianceCoreProperties { OPCPackage pkg; try { pkg = OPCPackage.open(is); - } catch (InvalidFormatException e) { - throw new RuntimeException(e); - } catch (IOException e) { + } catch (InvalidFormatException | IOException e) { throw new RuntimeException(e); } URI partUri = createURI("/docProps/core2.xml"); diff --git a/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java b/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java index 846335ce90..fe76115b28 100644 --- a/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java +++ b/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java @@ -74,7 +74,7 @@ public class TestFonts { // currently linux and mac return quite different values private static final int[] expected_sizes = { 304, // windows 10, 1080p, MS Office 2016, system text scaling 100% instead of default 125% - 306, // Windows 10, 15.6" 3840x2160 + 306, 308,// Windows 10, 15.6" 3840x2160 311, 312, 313, 318, 348, // Windows 10, 15.6" 3840x2160 362, // Windows 10, 13.3" 1080p high-dpi diff --git a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java index ade80688d0..c4c68b9717 100644 --- a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java +++ b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java @@ -17,18 +17,25 @@ package org.apache.poi.ss; -import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.FileNotFoundException; import org.apache.poi.EmptyFileException; import org.apache.poi.EncryptedDocumentException; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.POIDataSamples; import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; @@ -36,16 +43,14 @@ import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.util.TempFile; import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.xssf.usermodel.XSSFWorkbookFactory; import org.junit.Test; public final class TestWorkbookFactory { private static final String xls = "SampleSS.xls"; private static final String xlsx = "SampleSS.xlsx"; - private static final String[] xls_prot = new String[] {"password.xls", "password"}; - private static final String[] xlsx_prot = new String[]{"protected_passtika.xlsx", "tika"}; + private static final String[] xls_protected = new String[] {"password.xls", "password"}; + private static final String[] xlsx_protected = new String[]{"protected_passtika.xlsx", "tika"}; private static final String txt = "SampleSS.txt"; private static final POILogger LOGGER = POILogFactory.getLogger(TestWorkbookFactory.class); @@ -105,6 +110,13 @@ public final class TestWorkbookFactory { assertTrue(wb instanceof HSSFWorkbook); assertCloseDoesNotModifyFile(xls, wb); + wb = WorkbookFactory.create( + new POIFSFileSystem(HSSFTestDataSamples.openSampleFileStream(xls)).getRoot() + ); + assertNotNull(wb); + assertTrue(wb instanceof HSSFWorkbook); + assertCloseDoesNotModifyFile(xls, wb); + // Package -> xssf wb = XSSFWorkbookFactory.create( OPCPackage.open( @@ -195,7 +207,6 @@ public final class TestWorkbookFactory { public void testCreateWithPasswordFromStream() throws Exception { Workbook wb; - // Unprotected, no password given, opens normally wb = WorkbookFactory.create( HSSFTestDataSamples.openSampleFileStream(xls), null @@ -230,26 +241,26 @@ public final class TestWorkbookFactory { // Protected, correct password, opens fine wb = WorkbookFactory.create( - HSSFTestDataSamples.openSampleFileStream(xls_prot[0]), xls_prot[1] + HSSFTestDataSamples.openSampleFileStream(xls_protected[0]), xls_protected[1] ); assertNotNull(wb); assertTrue(wb instanceof HSSFWorkbook); - assertCloseDoesNotModifyFile(xls_prot[0], wb); + assertCloseDoesNotModifyFile(xls_protected[0], wb); wb = WorkbookFactory.create( - HSSFTestDataSamples.openSampleFileStream(xlsx_prot[0]), xlsx_prot[1] + HSSFTestDataSamples.openSampleFileStream(xlsx_protected[0]), xlsx_protected[1] ); assertNotNull(wb); assertTrue(wb instanceof XSSFWorkbook); - assertCloseDoesNotModifyFile(xlsx_prot[0], wb); + assertCloseDoesNotModifyFile(xlsx_protected[0], wb); // Protected, wrong password, throws Exception try { wb = WorkbookFactory.create( - HSSFTestDataSamples.openSampleFileStream(xls_prot[0]), "wrong" + HSSFTestDataSamples.openSampleFileStream(xls_protected[0]), "wrong" ); - assertCloseDoesNotModifyFile(xls_prot[0], wb); + assertCloseDoesNotModifyFile(xls_protected[0], wb); fail("Shouldn't be able to open with the wrong password"); } catch (EncryptedDocumentException e) { // expected here @@ -257,9 +268,9 @@ public final class TestWorkbookFactory { try { wb = WorkbookFactory.create( - HSSFTestDataSamples.openSampleFileStream(xlsx_prot[0]), "wrong" + HSSFTestDataSamples.openSampleFileStream(xlsx_protected[0]), "wrong" ); - assertCloseDoesNotModifyFile(xlsx_prot[0], wb); + assertCloseDoesNotModifyFile(xlsx_protected[0], wb); fail("Shouldn't be able to open with the wrong password"); } catch (EncryptedDocumentException e) { // expected here @@ -305,28 +316,28 @@ public final class TestWorkbookFactory { // Protected, correct password, opens fine wb = WorkbookFactory.create( - HSSFTestDataSamples.getSampleFile(xls_prot[0]), xls_prot[1] + HSSFTestDataSamples.getSampleFile(xls_protected[0]), xls_protected[1] ); assertNotNull(wb); assertTrue(wb instanceof HSSFWorkbook); - assertCloseDoesNotModifyFile(xls_prot[0], wb); + assertCloseDoesNotModifyFile(xls_protected[0], wb); wb = WorkbookFactory.create( - HSSFTestDataSamples.getSampleFile(xlsx_prot[0]), xlsx_prot[1] + HSSFTestDataSamples.getSampleFile(xlsx_protected[0]), xlsx_protected[1] ); assertNotNull(wb); assertTrue(wb instanceof XSSFWorkbook); assertTrue(wb.getNumberOfSheets() > 0); assertNotNull(wb.getSheetAt(0)); assertNotNull(wb.getSheetAt(0).getRow(0)); - assertCloseDoesNotModifyFile(xlsx_prot[0], wb); + assertCloseDoesNotModifyFile(xlsx_protected[0], wb); // Protected, wrong password, throws Exception try { wb = WorkbookFactory.create( - HSSFTestDataSamples.getSampleFile(xls_prot[0]), "wrong" + HSSFTestDataSamples.getSampleFile(xls_protected[0]), "wrong" ); - assertCloseDoesNotModifyFile(xls_prot[0], wb); + assertCloseDoesNotModifyFile(xls_protected[0], wb); fail("Shouldn't be able to open with the wrong password"); } catch (EncryptedDocumentException e) { // expected here @@ -334,9 +345,9 @@ public final class TestWorkbookFactory { try { wb = WorkbookFactory.create( - HSSFTestDataSamples.getSampleFile(xlsx_prot[0]), "wrong" + HSSFTestDataSamples.getSampleFile(xlsx_protected[0]), "wrong" ); - assertCloseDoesNotModifyFile(xlsx_prot[0], wb); + assertCloseDoesNotModifyFile(xlsx_protected[0], wb); fail("Shouldn't be able to open with the wrong password"); } catch (EncryptedDocumentException e) { // expected here @@ -387,4 +398,59 @@ public final class TestWorkbookFactory { } } + /** + * See Bugzilla bug #62831 - #WorkbookFactory.create(File) needs + * to work for sub-classes of File too, eg JFileChooser + */ + @Test + public void testFileSubclass() throws Exception { + File normalXLS = HSSFTestDataSamples.getSampleFile(xls); + File normalXLSX = HSSFTestDataSamples.getSampleFile(xlsx); + File altXLS = new TestFile(normalXLS.getAbsolutePath()); + File altXLSX = new TestFile(normalXLSX.getAbsolutePath()); + assertTrue(altXLS.exists()); + assertTrue(altXLSX.exists()); + + Workbook wb = WorkbookFactory.create(altXLS); + assertNotNull(wb); + assertTrue(wb instanceof HSSFWorkbook); + closeOrRevert(wb); + + wb = WorkbookFactory.create(altXLSX); + assertNotNull(wb); + assertTrue(wb instanceof XSSFWorkbook); + closeOrRevert(wb); + } + + private static class TestFile extends File { + public TestFile(String file) { + super(file); + } + } + + /** + * Check that the overloaded file methods which take passwords work properly + */ + @Test + public void testCreateEmpty() throws Exception { + Workbook wb = WorkbookFactory.create(false); + assertTrue(wb instanceof HSSFWorkbook); + closeOrRevert(wb); + + wb = WorkbookFactory.create(true); + assertTrue(wb instanceof XSSFWorkbook); + closeOrRevert(wb); + } + + @Test + public void testInvalidFormatException() { + String filename = "OPCCompliance_DerivedPartNameFAIL.docx"; + try { + WorkbookFactory.create(POIDataSamples.getOpenXML4JInstance().openResourceAsStream(filename)); + fail("Expecting an Exception for this document"); + } catch (IOException e) { + // expected here + } + } + } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java index e03dfaaf36..ba26fb8e95 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java @@ -47,22 +47,22 @@ public class TestXSLFTextParagraph { DrawTextParagraphProxy(XSLFTextParagraph p) { super(p); } - + @Override public void breakText(Graphics2D graphics) { super.breakText(graphics); } - + @Override public double getWrappingWidth(boolean firstLine, Graphics2D graphics) { return super.getWrappingWidth(firstLine, graphics); } - + public List<DrawTextFragment> getLines() { return lines; } } - + @Test public void testWrappingWidth() throws IOException { XMLSlideShow ppt = new XMLSlideShow(); @@ -78,11 +78,11 @@ public class TestXSLFTextParagraph { Rectangle2D anchor = new Rectangle2D.Double(50, 50, 300, 200); sh.setAnchor(anchor); - + DrawTextParagraphProxy dtp = new DrawTextParagraphProxy(p); - Double leftInset = sh.getLeftInset(); - Double rightInset = sh.getRightInset(); + double leftInset = sh.getLeftInset(); + double rightInset = sh.getRightInset(); assertEquals(7.2, leftInset, 0); assertEquals(7.2, rightInset, 0); @@ -142,13 +142,13 @@ public class TestXSLFTextParagraph { indent = p.getIndent(); assertEquals(-72.0, indent, 0); expectedWidth = anchor.getWidth() - leftInset - rightInset; - assertEquals(280.0, expectedWidth, 0); // 300 - 10 - 10 + assertEquals(280.0, expectedWidth, 0); // 300 - 10 - 10 assertEquals(expectedWidth, dtp.getWrappingWidth(true, null), 0); // first line is NOT indented // other lines are indented by leftMargin (the value of indent is not used) expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin; - assertEquals(244.0, expectedWidth, 0); // 300 - 10 - 10 - 36 + assertEquals(244.0, expectedWidth, 0); // 300 - 10 - 10 - 36 assertEquals(expectedWidth, dtp.getWrappingWidth(false, null), 0); - + ppt.close(); } @@ -294,13 +294,13 @@ public class TestXSLFTextParagraph { assertEquals(-20.0, p.getBulletFontSize(), 0); assertEquals(72.0, p.getDefaultTabSize(), 0); - + assertNull(p.getIndent()); p.setIndent(72.0); assertEquals(72.0, p.getIndent(), 0); p.setIndent(-1d); // the value of -1.0 resets to the defaults (not any more ...) assertEquals(-1d, p.getIndent(), 0); - p.setIndent(null); + p.setIndent(null); assertNull(p.getIndent()); assertEquals(0.0, p.getLeftMargin(), 0); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java index 048e44a8e3..0f03af7453 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java @@ -28,13 +28,12 @@ import java.io.InputStream; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.util.IOUtils; import org.apache.poi.util.TempFile; import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** - * Centralises logic for finding/opening sample files in the test-data/spreadsheet folder. - * + * Centralises logic for finding/opening sample files in the test-data/spreadsheet folder. + * * @author Josh Micich */ public class XSSFTestDataSamples { @@ -63,7 +62,7 @@ public class XSSFTestDataSamples { throw new RuntimeException(e); } } - + /** * Write out workbook <code>wb</code> to {@link #TEST_OUTPUT_DIR}/testName.xlsx * (or create a temporary file if <code>TEST_OUTPUT_DIR</code> is not defined). @@ -78,11 +77,13 @@ public class XSSFTestDataSamples { writeOut(wb, file); return file; } - + private static <R extends Workbook> void writeOut(R wb, File file) throws IOException { - IOUtils.write(wb, new FileOutputStream(file)); + try (FileOutputStream out = new FileOutputStream(file)) { + wb.write(out); + } } - + // Anticipates the location of where a workbook will be written to // Note that if TEST_OUTPUT_DIR is not set, this will create temporary files // with unique names. Subsequent calls with the same argument may return a different file. @@ -107,7 +108,7 @@ public class XSSFTestDataSamples { } return file; } - + /** * Write out workbook <code>wb</code> to a memory buffer * @@ -120,18 +121,18 @@ public class XSSFTestDataSamples { wb.write(out); return out; } - + /** - * Write out the workbook then closes the workbook. + * Write out the workbook then closes the workbook. * This should be used when there is insufficient memory to have * both workbooks open. - * + * * Make sure there are no references to any objects in the workbook * so that garbage collection may free the workbook. - * + * * After calling this method, null the reference to <code>wb</code>, * then call {@link #readBack(File)} or {@link #readBackAndDelete(File)} to re-read the file. - * + * * Alternatively, use {@link #writeOutAndClose(Workbook)} to use a ByteArrayOutputStream/ByteArrayInputStream * to avoid creating a temporary file. However, this may complicate the calling * code to avoid having the workbook, BAOS, and BAIS open at the same time. @@ -152,8 +153,8 @@ public class XSSFTestDataSamples { throw new RuntimeException(e); } } - - + + /** * Write out workbook <code>wb</code> to a memory buffer, * then close the workbook @@ -173,7 +174,7 @@ public class XSSFTestDataSamples { throw new RuntimeException(e); } } - + /** * Read back a workbook that was written out to a file with * {@link #writeOut(Workbook, String))} or {@link #writeOutAndClose(Workbook, String)}. @@ -186,11 +187,11 @@ public class XSSFTestDataSamples { */ public static XSSFWorkbook readBackAndDelete(File file) throws IOException { XSSFWorkbook wb = readBack(file); - // do not delete the file if there's an error--might be helpful for debugging + // do not delete the file if there's an error--might be helpful for debugging file.delete(); return wb; } - + /** * Read back a workbook that was written out to a file with * {@link #writeOut(Workbook, String)} or {@link #writeOutAndClose(Workbook, String)}. @@ -208,12 +209,12 @@ public class XSSFTestDataSamples { in.close(); } } - + /** * Read back a workbook that was written out to a memory buffer with * {@link #writeOut(Workbook)} or {@link #writeOutAndClose(Workbook)}. * - * @param file the workbook file to read + * @param out the output stream to read back from * @return the read back workbook * @throws IOException */ @@ -227,15 +228,15 @@ public class XSSFTestDataSamples { is.close(); } } - + /** * Write out and read back using a memory buffer to avoid disk I/O. * If there is not enough memory to have two workbooks open at the same time, * consider using: - * + * * Workbook wb = new XSSFWorkbook(); * String testName = "example"; - * + * * <code> * File file = writeOutAndClose(wb, testName); * // clear all references that would prevent the workbook from getting garbage collected @@ -257,7 +258,7 @@ public class XSSFTestDataSamples { R r = (R) result; return r; } - + /** * Write out, close, and read back the workbook using a memory buffer to avoid disk I/O. * @@ -274,18 +275,18 @@ public class XSSFTestDataSamples { @SuppressWarnings("unchecked") R r = (R) result; return r; - + } - + /** - * Writes the Workbook either into a file or into a byte array, depending on presence of + * Writes the Workbook either into a file or into a byte array, depending on presence of * the system property {@value #TEST_OUTPUT_DIR}, and reads it in a new instance of the Workbook back. * If TEST_OUTPUT_DIR is set, the file will NOT be deleted at the end of this function. * @param wb workbook to write * @param testName file name to be used if writing into a file. The old file with the same name will be overridden. * @return new instance read from the stream written by the wb parameter. */ - + public static <R extends Workbook> R writeOutAndReadBack(R wb, String testName) { if (System.getProperty(TEST_OUTPUT_DIR) == null) { return writeOutAndReadBack(wb); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFBEventBasedExcelExtractor.java b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFBEventBasedExcelExtractor.java index 4fd9a5c1d7..e173876188 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFBEventBasedExcelExtractor.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/extractor/TestXSSFBEventBasedExcelExtractor.java @@ -26,6 +26,10 @@ import static org.junit.Assert.assertTrue; import org.apache.poi.xssf.XSSFTestDataSamples; import org.junit.Test; +import java.io.BufferedReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + /** * Tests for {@link XSSFBEventBasedExcelExtractor} */ @@ -110,4 +114,23 @@ public class TestXSSFBEventBasedExcelExtractor { "This is an example spreadsheet created with Microsoft Excel 2007 Beta 2."); } + @Test + public void test62815() throws Exception { + //test file based on http://oss.sheetjs.com/test_files/RkNumber.xlsb + XSSFEventBasedExcelExtractor extractor = getExtractor("62815.xlsb"); + extractor.setIncludeCellComments(true); + String[] rows = extractor.getText().split("[\r\n]+"); + assertEquals(283, rows.length); + BufferedReader reader = Files.newBufferedReader(XSSFTestDataSamples.getSampleFile("62815.xlsb.txt").toPath(), + StandardCharsets.UTF_8); + String line = reader.readLine(); + for (int i = 0; i < rows.length; i++) { + assertEquals(line, rows[i]); + line = reader.readLine(); + while (line != null && line.startsWith("#")) { + line = reader.readLine(); + } + } + } + } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbook.java b/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbook.java index 120fe0b5b2..de4348c9d5 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbook.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbook.java @@ -38,7 +38,6 @@ import java.util.Arrays; import org.apache.poi.POIDataSamples; import org.apache.poi.POITestCase; -import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackageAccess; import org.apache.poi.ss.usermodel.BaseTestXWorkbook; @@ -55,7 +54,6 @@ import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.After; -import org.junit.Assume; import org.junit.Ignore; import org.junit.Test; @@ -103,9 +101,9 @@ public final class TestSXSSFWorkbook extends BaseTestXWorkbook { * changes. */ @Override + @Ignore("SXSSF doesn't update formulas on sheet name changes, as most cells probably aren't in memory at the time") @Test public void setSheetName() { - Assume.assumeTrue("SXSSF doesn't update formulas on sheet name changes, as most cells probably aren't in memory at the time", false); } @Test @@ -374,7 +372,7 @@ public final class TestSXSSFWorkbook extends BaseTestXWorkbook { @Test public void bug53515a() throws Exception { File out = new File("Test.xlsx"); - out.delete(); + assertTrue(!out.exists() || out.delete()); for (int i = 0; i < 2; i++) { final SXSSFWorkbook wb; if (out.exists()) { @@ -402,7 +400,8 @@ public final class TestSXSSFWorkbook extends BaseTestXWorkbook { } wb.close(); } - out.delete(); + assertTrue(out.exists()); + assertTrue(out.delete()); } private static void populateWorkbook(Workbook wb) { @@ -500,33 +499,37 @@ public final class TestSXSSFWorkbook extends BaseTestXWorkbook { @Test @Ignore public void createFromReadOnlyWorkbook() throws Exception { - File input = XSSFTestDataSamples.getSampleFile("sample.xlsx"); - OPCPackage pkg = OPCPackage.open(input, PackageAccess.READ); - XSSFWorkbook xssf = new XSSFWorkbook(pkg); - SXSSFWorkbook wb = new SXSSFWorkbook(xssf, 2); - String sheetName = "Test SXSSF"; - Sheet s = wb.createSheet(sheetName); - for (int i=0; i<10; i++) { - Row r = s.createRow(i); - r.createCell(0).setCellValue(true); - r.createCell(1).setCellValue(2.4); - r.createCell(2).setCellValue("Test Row " + i); + File input = XSSFTestDataSamples.getSampleFile("sample.xlsx"); + + try (OPCPackage pkg = OPCPackage.open(input, PackageAccess.READ)) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try (XSSFWorkbook xssf = new XSSFWorkbook(pkg)) { + try (SXSSFWorkbook wb = new SXSSFWorkbook(xssf, 2)) { + Sheet s = wb.createSheet(sheetName); + for (int i = 0; i < 10; i++) { + Row r = s.createRow(i); + r.createCell(0).setCellValue(true); + r.createCell(1).setCellValue(2.4); + r.createCell(2).setCellValue("Test Row " + i); + } + assertEquals(10, s.getLastRowNum()); + + wb.write(bos); + wb.dispose(); + } + } + + try (XSSFWorkbook xssf = new XSSFWorkbook(new ByteArrayInputStream(bos.toByteArray()))) { + Sheet s = xssf.getSheet(sheetName); + assertEquals(10, s.getLastRowNum()); + assertTrue(s.getRow(0).getCell(0).getBooleanCellValue()); + assertEquals("Test Row 9", s.getRow(9).getCell(2).getStringCellValue()); + } } - assertEquals(10, s.getLastRowNum()); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - wb.write(bos); - wb.dispose(); - wb.close(); - - xssf = new XSSFWorkbook(new ByteArrayInputStream(bos.toByteArray())); - s = xssf.getSheet(sheetName); - assertEquals(10, s.getLastRowNum()); - assertTrue(s.getRow(0).getCell(0).getBooleanCellValue()); - assertEquals("Test Row 9", s.getRow(9).getCell(2).getStringCellValue()); } + @Test public void test56557() throws IOException { Workbook wb = XSSFTestDataSamples.openSampleWorkbook("56557.xlsx"); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java index 70a5092a07..ef83956468 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java @@ -47,15 +47,15 @@ import java.util.TreeMap; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; import org.apache.poi.POIDataSamples; -import org.apache.poi.ooxml.POIXMLDocumentPart; -import org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart; -import org.apache.poi.ooxml.POIXMLException; -import org.apache.poi.ooxml.POIXMLProperties; import org.apache.poi.common.usermodel.HyperlinkType; import org.apache.poi.hssf.HSSFITestDataProvider; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart; +import org.apache.poi.ooxml.POIXMLException; +import org.apache.poi.ooxml.POIXMLProperties; import org.apache.poi.ooxml.util.DocumentHelper; import org.apache.poi.ooxml.util.SAXHelper; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; @@ -82,7 +82,31 @@ import org.apache.poi.ss.formula.eval.ErrorEval; import org.apache.poi.ss.formula.eval.NumberEval; import org.apache.poi.ss.formula.functions.Function; import org.apache.poi.ss.formula.ptg.Ptg; -import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.usermodel.BaseTestBugzillaIssues; +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.CellType; +import org.apache.poi.ss.usermodel.CellValue; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.Comment; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.FormulaError; +import org.apache.poi.ss.usermodel.FormulaEvaluator; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Name; +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.SheetConditionalFormatting; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.CellAddress; import org.apache.poi.ss.util.CellRangeAddress; @@ -2261,7 +2285,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { * problems when deleting columns, conditionally to stop recursion */ private static final String FORMULA1 = - "IF( INDIRECT( ADDRESS( ROW(), COLUMN()-1 ) ) = 0, 0," + "IF( INDIRECT( ADDRESS( ROW(), COLUMN()-1 ) ) = 0, 0, " + "INDIRECT( ADDRESS( ROW(), COLUMN()-1 ) ) ) + 2"; /** @@ -2269,7 +2293,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { * problems when deleting rows, conditionally to stop recursion */ private static final String FORMULA2 = - "IF( INDIRECT( ADDRESS( ROW()-1, COLUMN() ) ) = 0, 0," + "IF( INDIRECT( ADDRESS( ROW()-1, COLUMN() ) ) = 0, 0, " + "INDIRECT( ADDRESS( ROW()-1, COLUMN() ) ) ) + 2"; /** @@ -2847,7 +2871,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { @Test public void test57236() throws IOException { // Having very small numbers leads to different formatting, Excel uses the scientific notation, but POI leads to "0" - + /* DecimalFormat format = new DecimalFormat("#.##########", new DecimalFormatSymbols(Locale.getDefault())); double d = 3.0E-104; @@ -2969,13 +2993,21 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { cell.setCellStyle(style); // Everything is fine at this point, cell is red + XSSFColor actual = cell.getCellStyle().getFillBackgroundColorColor(); + assertNull(actual); + actual = cell.getCellStyle().getFillForegroundColorColor(); + assertNotNull(actual); + assertEquals(color.getARGBHex(), actual.getARGBHex()); Map<String, Object> properties = new HashMap<>(); properties.put(CellUtil.BORDER_BOTTOM, BorderStyle.THIN); CellUtil.setCellStyleProperties(cell, properties); // Now the cell is all black - XSSFColor actual = cell.getCellStyle().getFillBackgroundColorColor(); + actual = cell.getCellStyle().getFillBackgroundColorColor(); + assertNotNull(actual); + assertNull(actual.getARGBHex()); + actual = cell.getCellStyle().getFillForegroundColorColor(); assertNotNull(actual); assertEquals(color.getARGBHex(), actual.getARGBHex()); @@ -3290,7 +3322,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { wb.close(); } - + /** * Auto column sizing failed when there were loads of fonts with * errors like ArrayIndexOutOfBoundsException: -32765 @@ -3300,7 +3332,7 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { XSSFWorkbook wb = new XSSFWorkbook(); XSSFSheet sheet = wb.createSheet(); XSSFRow row = sheet.createRow(0); - + // Create lots of fonts XSSFDataFormat formats = wb.createDataFormat(); XSSFFont[] fonts = new XSSFFont[50000]; @@ -3309,23 +3341,23 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues { font.setFontHeight(i); fonts[i] = font; } - + // Create a moderate number of columns, which use // fonts from the start and end of the font list final int numCols = 125; for (int i=0; i<numCols; i++) { XSSFCellStyle cs = wb.createCellStyle(); cs.setDataFormat(formats.getFormat("'Test "+i+"' #,###")); - + XSSFFont font = fonts[i]; if (i%2==1) { font = fonts[fonts.length-i]; } cs.setFont(font); - + XSSFCell c = row.createCell(i); c.setCellValue(i); c.setCellStyle(cs); } - + // Do the auto-size for (int i=0; i<numCols; i++) { sheet.autoSizeColumn(i); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColGrouping.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColGrouping.java index 9811c403bd..abd769aed7 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColGrouping.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFColGrouping.java @@ -33,20 +33,20 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols; * Test asserts the POI produces <cols> element that could be read and properly interpreted by the MS Excel. * For specification of the "cols" element see the chapter 3.3.1.16 of the "Office Open XML Part 4 - Markup Language Reference.pdf". * The specification can be downloaded at http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%201st%20edition%20Part%204%20(PDF).zip. - * + * * <p><em> * The test saves xlsx file on a disk if the system property is set: * -Dpoi.test.xssf.output.dir=${workspace_loc}/poi/build/xssf-output * </em> - * + * */ public class TestXSSFColGrouping { - + private static final POILogger logger = POILogFactory.getLogger(TestXSSFColGrouping.class); - + /** - * Tests that POI doesn't produce "col" elements without "width" attribute. + * Tests that POI doesn't produce "col" elements without "width" attribute. * POI-52186 */ @Test @@ -56,25 +56,25 @@ public class TestXSSFColGrouping { sheet.setColumnWidth(4, 5000); sheet.setColumnWidth(5, 5000); - + sheet.groupColumn((short) 4, (short) 7); sheet.groupColumn((short) 9, (short) 12); - + XSSFWorkbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(wb1, "testNoColsWithoutWidthWhenGrouping"); sheet = wb2.getSheet("test"); - + CTCols cols = sheet.getCTWorksheet().getColsArray(0); logger.log(POILogger.DEBUG, "test52186/cols:" + cols); for (CTCol col : cols.getColArray()) { assertTrue("Col width attribute is unset: " + col, col.isSetWidth()); } - + wb2.close(); wb1.close(); } /** - * Tests that POI doesn't produce "col" elements without "width" attribute. + * Tests that POI doesn't produce "col" elements without "width" attribute. * POI-52186 */ @Test @@ -84,17 +84,17 @@ public class TestXSSFColGrouping { sheet.setColumnWidth(4, 5000); sheet.setColumnWidth(5, 5000); - + sheet.groupColumn((short) 4, (short) 5); - + sheet.setColumnGroupCollapsed(4, true); - + CTCols cols = sheet.getCTWorksheet().getColsArray(0); logger.log(POILogger.DEBUG, "test52186_2/cols:" + cols); XSSFWorkbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(wb1, "testNoColsWithoutWidthWhenGroupingAndCollapsing"); sheet = wb2.getSheet("test"); - + for (int i = 4; i <= 5; i++) { assertEquals("Unexpected width of column "+ i, 5000, sheet.getColumnWidth(i)); } @@ -105,7 +105,7 @@ public class TestXSSFColGrouping { wb2.close(); wb1.close(); } - + /** * Test the cols element is correct in case of NumericRanges.OVERLAPS_2_WRAPS */ @@ -122,10 +122,10 @@ public class TestXSSFColGrouping { col.setCustomWidth(true); sheet.groupColumn((short) 2, (short) 3); - + sheet.getCTWorksheet().getColsArray(0); logger.log(POILogger.DEBUG, "testMergingOverlappingCols_OVERLAPS_2_WRAPS/cols:" + cols); - + assertEquals(0, cols.getColArray(0).getOutlineLevel()); assertEquals(2, cols.getColArray(0).getMin()); // 1 based assertEquals(2, cols.getColArray(0).getMax()); // 1 based @@ -133,23 +133,23 @@ public class TestXSSFColGrouping { assertEquals(1, cols.getColArray(1).getOutlineLevel()); assertEquals(3, cols.getColArray(1).getMin()); // 1 based - assertEquals(4, cols.getColArray(1).getMax()); // 1 based + assertEquals(4, cols.getColArray(1).getMax()); // 1 based assertEquals(true, cols.getColArray(1).getCustomWidth()); assertEquals(0, cols.getColArray(2).getOutlineLevel()); assertEquals(5, cols.getColArray(2).getMin()); // 1 based assertEquals(5, cols.getColArray(2).getMax()); // 1 based assertEquals(true, cols.getColArray(2).getCustomWidth()); - + assertEquals(3, cols.sizeOfColArray()); XSSFWorkbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(wb1, "testMergingOverlappingCols_OVERLAPS_2_WRAPS"); sheet = wb2.getSheet("test"); - + for (int i = 1; i <= 4; i++) { assertEquals("Unexpected width of column "+ i, 20 * 256, sheet.getColumnWidth(i)); } - + wb2.close(); wb1.close(); } @@ -170,10 +170,10 @@ public class TestXSSFColGrouping { col.setCustomWidth(true); sheet.groupColumn((short) 1, (short) 5); - + cols = sheet.getCTWorksheet().getColsArray(0); logger.log(POILogger.DEBUG, "testMergingOverlappingCols_OVERLAPS_1_WRAPS/cols:" + cols); - + assertEquals(1, cols.getColArray(0).getOutlineLevel()); assertEquals(2, cols.getColArray(0).getMin()); // 1 based assertEquals(2, cols.getColArray(0).getMax()); // 1 based @@ -181,23 +181,23 @@ public class TestXSSFColGrouping { assertEquals(1, cols.getColArray(1).getOutlineLevel()); assertEquals(3, cols.getColArray(1).getMin()); // 1 based - assertEquals(5, cols.getColArray(1).getMax()); // 1 based + assertEquals(5, cols.getColArray(1).getMax()); // 1 based assertEquals(true, cols.getColArray(1).getCustomWidth()); assertEquals(1, cols.getColArray(2).getOutlineLevel()); assertEquals(6, cols.getColArray(2).getMin()); // 1 based assertEquals(6, cols.getColArray(2).getMax()); // 1 based assertEquals(false, cols.getColArray(2).getCustomWidth()); - + assertEquals(3, cols.sizeOfColArray()); - + XSSFWorkbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(wb1, "testMergingOverlappingCols_OVERLAPS_1_WRAPS"); sheet = wb2.getSheet("test"); - + for (int i = 2; i <= 4; i++) { assertEquals("Unexpected width of column "+ i, 20 * 256, sheet.getColumnWidth(i)); } - + wb2.close(); wb1.close(); } @@ -218,7 +218,7 @@ public class TestXSSFColGrouping { col.setCustomWidth(true); sheet.groupColumn((short) 3, (short) 5); - + cols = sheet.getCTWorksheet().getColsArray(0); logger.log(POILogger.DEBUG, "testMergingOverlappingCols_OVERLAPS_1_MINOR/cols:" + cols); @@ -229,24 +229,24 @@ public class TestXSSFColGrouping { assertEquals(1, cols.getColArray(1).getOutlineLevel()); assertEquals(4, cols.getColArray(1).getMin()); // 1 based - assertEquals(5, cols.getColArray(1).getMax()); // 1 based + assertEquals(5, cols.getColArray(1).getMax()); // 1 based assertEquals(true, cols.getColArray(1).getCustomWidth()); assertEquals(1, cols.getColArray(2).getOutlineLevel()); assertEquals(6, cols.getColArray(2).getMin()); // 1 based assertEquals(6, cols.getColArray(2).getMax()); // 1 based assertEquals(false, cols.getColArray(2).getCustomWidth()); - + assertEquals(3, cols.sizeOfColArray()); - + XSSFWorkbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(wb1, "testMergingOverlappingCols_OVERLAPS_1_MINOR"); sheet = wb2.getSheet("test"); - + for (int i = 2; i <= 4; i++) { - assertEquals("Unexpected width of column "+ i, 20 * 256, sheet.getColumnWidth(i)); + assertEquals("Unexpected width of column "+ i, 20 * 256L, sheet.getColumnWidth(i)); } - assertEquals("Unexpected width of column "+ 5, sheet.getDefaultColumnWidth() * 256, sheet.getColumnWidth(5)); - + assertEquals("Unexpected width of column "+ 5, sheet.getDefaultColumnWidth() * 256L, sheet.getColumnWidth(5)); + wb2.close(); wb1.close(); } @@ -267,7 +267,7 @@ public class TestXSSFColGrouping { col.setCustomWidth(true); sheet.groupColumn((short) 1, (short) 3); - + cols = sheet.getCTWorksheet().getColsArray(0); logger.log(POILogger.DEBUG, "testMergingOverlappingCols_OVERLAPS_2_MINOR/cols:" + cols); @@ -278,24 +278,24 @@ public class TestXSSFColGrouping { assertEquals(1, cols.getColArray(1).getOutlineLevel()); assertEquals(3, cols.getColArray(1).getMin()); // 1 based - assertEquals(4, cols.getColArray(1).getMax()); // 1 based + assertEquals(4, cols.getColArray(1).getMax()); // 1 based assertEquals(true, cols.getColArray(1).getCustomWidth()); assertEquals(0, cols.getColArray(2).getOutlineLevel()); assertEquals(5, cols.getColArray(2).getMin()); // 1 based assertEquals(5, cols.getColArray(2).getMax()); // 1 based assertEquals(true, cols.getColArray(2).getCustomWidth()); - + assertEquals(3, cols.sizeOfColArray()); - + XSSFWorkbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(wb1, "testMergingOverlappingCols_OVERLAPS_2_MINOR"); sheet = wb2.getSheet("test"); - + for (int i = 2; i <= 4; i++) { - assertEquals("Unexpected width of column "+ i, 20 * 256, sheet.getColumnWidth(i)); + assertEquals("Unexpected width of column "+ i, 20 * 256L, sheet.getColumnWidth(i)); } - assertEquals("Unexpected width of column "+ 1, sheet.getDefaultColumnWidth() * 256, sheet.getColumnWidth(1)); - + assertEquals("Unexpected width of column "+ 1, sheet.getDefaultColumnWidth() * 256L, sheet.getColumnWidth(1)); + wb2.close(); wb1.close(); } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDataValidation.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDataValidation.java index 3b7c2b8ce9..06d7866f8a 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDataValidation.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDataValidation.java @@ -23,7 +23,6 @@ import java.math.BigDecimal; import java.util.List; import org.apache.poi.ss.formula.DataValidationEvaluator; -import org.apache.poi.ss.formula.WorkbookEvaluator; import org.apache.poi.ss.formula.eval.ValueEval; import org.apache.poi.ss.usermodel.BaseTestDataValidation; import org.apache.poi.ss.usermodel.Cell; @@ -57,16 +56,16 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { /** * For each validation type, there are two cells with the same validation. This tests * application of a single validation definition to multiple cells. - * + * * For list ( 3 validations for explicit and 3 for formula ) - * - one validation that allows blank. + * - one validation that allows blank. * - one that does not allow blank. * - one that does not show the drop down arrow. * = 2 - * + * * For number validations ( integer/decimal and text length ) with 8 different types of operators. - * = 50 - * + * = 50 + * * = 52 ( Total ) */ assertEquals(52,dataValidations.size()); @@ -140,7 +139,7 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { cell_10.setCellValue(XSSFDataValidation.operatorTypeMappings.get(operatorType).toString()); Cell cell_11 = row1.createCell(1); Cell cell_21 = row1.createCell(2); - Cell cell_22 = i==0 && j < 2 ? row2.createCell(2) : null; + Cell cell_22 = i==0 && j < 2 ? (row2 == null ? null : row2.createCell(2)) : null; Cell cell_13 = row1.createCell(3); @@ -170,7 +169,9 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { assertEquals(++lastKnownNumValidations, ((XSSFSheet) sheet).getDataValidations().size()); cellRangeAddressList = new CellRangeAddressList(); - cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_22.getRowIndex(), cell_22.getRowIndex(), cell_22.getColumnIndex(), cell_22.getColumnIndex())); + if (cell_22 != null) { + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_22.getRowIndex(), cell_22.getRowIndex(), cell_22.getColumnIndex(), cell_22.getColumnIndex())); + } validation = dataValidationHelper.createValidation(constraint, cellRangeAddressList); setOtherValidationParameters( validation); sheet.addValidationData(validation); @@ -178,7 +179,9 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { } else if(i==0 && j==1 ){ cellRangeAddressList = new CellRangeAddressList(); cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_21.getRowIndex(), cell_21.getRowIndex(), cell_21.getColumnIndex(), cell_21.getColumnIndex())); - cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_22.getRowIndex(), cell_22.getRowIndex(), cell_22.getColumnIndex(), cell_22.getColumnIndex())); + if (cell_22 != null) { + cellRangeAddressList.addCellRangeAddress(new CellRangeAddress(cell_22.getRowIndex(), cell_22.getRowIndex(), cell_22.getColumnIndex(), cell_22.getColumnIndex())); + } validation = dataValidationHelper.createValidation(constraint, cellRangeAddressList); setOtherValidationParameters( validation); sheet.addValidationData(validation); @@ -262,16 +265,16 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { XSSFSheet sheet = wb.createSheet(); List<XSSFDataValidation> lst = sheet.getDataValidations(); //<-- works assertEquals(0, lst.size()); - + //create the cell that will have the validation applied sheet.createRow(0).createCell(0); - + DataValidationHelper dataValidationHelper = sheet.getDataValidationHelper(); DataValidationConstraint constraint = dataValidationHelper.createCustomConstraint("SUM($A$1:$A$1) <= 3500"); CellRangeAddressList addressList = new CellRangeAddressList(0, 0, 0, 0); DataValidation validation = dataValidationHelper.createValidation(constraint, addressList); sheet.addValidationData(validation); - + // this line caused XmlValueOutOfRangeException , see Bugzilla 3965 lst = sheet.getDataValidations(); assertEquals(1, lst.size()); @@ -282,10 +285,10 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { public void testDefaultErrorStyle() throws IOException { try (XSSFWorkbook wb = new XSSFWorkbook()) { XSSFSheet sheet = wb.createSheet(); - + final XSSFDataValidation validation = createValidation(sheet); sheet.addValidationData(validation); - + final List<XSSFDataValidation> dataValidations = sheet.getDataValidations(); assertEquals(DataValidation.ErrorStyle.STOP, dataValidations.get(0).getErrorStyle()); } @@ -295,22 +298,22 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { public void testSetErrorStyles() throws IOException { try (XSSFWorkbook wb = new XSSFWorkbook()) { XSSFSheet sheet = wb.createSheet(); - + XSSFDataValidation validation = createValidation(sheet); sheet.addValidationData(validation); - + // extract generated validation from sheet List<XSSFDataValidation> dataValidations = sheet.getDataValidations(); validation = dataValidations.get(0); - + // test INFO validation.setErrorStyle(DataValidation.ErrorStyle.INFO); assertEquals(DataValidation.ErrorStyle.INFO, dataValidations.get(0).getErrorStyle()); - + // test WARNING validation.setErrorStyle(DataValidation.ErrorStyle.WARNING); assertEquals(DataValidation.ErrorStyle.WARNING, dataValidations.get(0).getErrorStyle()); - + // test STOP validation.setErrorStyle(DataValidation.ErrorStyle.STOP); assertEquals(DataValidation.ErrorStyle.STOP, dataValidations.get(0).getErrorStyle()); @@ -321,10 +324,10 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { public void testDefaultAllowBlank() throws IOException { try (XSSFWorkbook wb = new XSSFWorkbook()) { XSSFSheet sheet = wb.createSheet(); - + final XSSFDataValidation validation = createValidation(sheet); sheet.addValidationData(validation); - + final List<XSSFDataValidation> dataValidations = sheet.getDataValidations(); assertEquals(true, dataValidations.get(0).getCtDdataValidation().getAllowBlank()); } @@ -334,12 +337,12 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { public void testSetAllowBlankToFalse() throws IOException { try (XSSFWorkbook wb = new XSSFWorkbook()) { XSSFSheet sheet = wb.createSheet(); - + final XSSFDataValidation validation = createValidation(sheet); validation.getCtDdataValidation().setAllowBlank(false); - + sheet.addValidationData(validation); - + final List<XSSFDataValidation> dataValidations = sheet.getDataValidations(); assertEquals(false, dataValidations.get(0).getCtDdataValidation().getAllowBlank()); } @@ -349,12 +352,12 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { public void testSetAllowBlankToTrue() throws IOException { try (XSSFWorkbook wb = new XSSFWorkbook()) { XSSFSheet sheet = wb.createSheet(); - + final XSSFDataValidation validation = createValidation(sheet); validation.getCtDdataValidation().setAllowBlank(true); - + sheet.addValidationData(validation); - + final List<XSSFDataValidation> dataValidations = sheet.getDataValidations(); assertEquals(true, dataValidations.get(0).getCtDdataValidation().getAllowBlank()); } @@ -370,7 +373,7 @@ public class TestXSSFDataValidation extends BaseTestDataValidation { DataValidationConstraint constraint = dataValidationHelper.createCustomConstraint("true"); return (XSSFDataValidation) dataValidationHelper.createValidation(constraint, new CellRangeAddressList(0, 0, 0, 0)); } - + @Test public void testTableBasedValidationList() throws IOException { try (XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("dataValidationTableRange.xlsx")) { diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java index f8f71686fa..418eb268e8 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFFormulaEvaluation.java @@ -17,14 +17,24 @@ package org.apache.poi.xssf.usermodel; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.apache.poi.hssf.HSSFTestDataSamples; -import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.usermodel.BaseTestFormulaEvaluator; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellValue; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.FormulaEvaluator; +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.ss.util.CellReference; import org.apache.poi.xssf.XSSFITestDataProvider; import org.apache.poi.xssf.XSSFTestDataSamples; @@ -437,6 +447,10 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator { assertEquals("D 0,068", evaluator.evaluate(wb.getSheetAt(0).getRow(1).getCell(1))); } + /** + * see bug 62275 + * @throws IOException + */ @Test public void testBug62275() throws IOException { try (Workbook wb = new XSSFWorkbook()) { @@ -451,4 +465,29 @@ public final class TestXSSFFormulaEvaluation extends BaseTestFormulaEvaluator { eval.evaluate(cell); } } + + /** + * see bug 62834, handle when a shared formula range doesn't contain only formula cells + * @throws IOException + */ + @Test + public void testBug62834() throws IOException { + try (Workbook wb = XSSFTestDataSamples.openSampleWorkbook("62834.xlsx")) { + FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); + + Cell a2 = wb.getSheetAt(0).getRow(1).getCell(0); + Cell value = evaluator.evaluateInCell(a2); + assertEquals("wrong value A2", "a value", value.getStringCellValue()); + +// evaluator.clearAllCachedResultValues(); + + Cell a3 = wb.getSheetAt(0).getRow(2).getCell(0); + value = evaluator.evaluateInCell(a3); + assertEquals("wrong value A3", "a value", value.getStringCellValue()); + + Cell a5 = wb.getSheetAt(0).getRow(4).getCell(0); + value = evaluator.evaluateInCell(a5); + assertEquals("wrong value A5", "another value", value.getStringCellValue()); + } + } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java index 239ed9ab2d..63fbf8b616 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java @@ -36,8 +36,8 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.poi.ooxml.POIXMLException; import org.apache.poi.hssf.HSSFTestDataSamples; +import org.apache.poi.ooxml.POIXMLException; import org.apache.poi.poifs.crypt.CryptoFunctions; import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.ss.usermodel.AutoFilter; @@ -158,7 +158,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals("", ftr.getLeft()); assertEquals("", ftr.getCenter()); assertEquals("", ftr.getRight()); - + wb2.close(); } @@ -200,7 +200,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { // Defaults are odd assertEquals("odd footer left", sheet.getFooter().getLeft()); assertEquals("odd header center", sheet.getHeader().getCenter()); - + workbook.close(); } @@ -265,7 +265,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { sheet.createSplitPane(4, 8, 12, 12, 1); assertEquals(8.0, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getYSplit(), 0.0); assertEquals(STPane.BOTTOM_RIGHT, ctWorksheet.getSheetViews().getSheetViewArray(0).getPane().getActivePane()); - + workbook.close(); } @@ -374,7 +374,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { colArray = cols.getColArray(); assertEquals(4, colArray.length); assertEquals(2, sheet.getCTWorksheet().getSheetFormatPr().getOutlineLevelCol()); - + workbook.close(); } @@ -411,7 +411,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals(3, sheet.getPhysicalNumberOfRows()); assertEquals(1, sheet.getCTWorksheet().getSheetFormatPr().getOutlineLevelRow()); - + workbook.close(); } @@ -553,10 +553,10 @@ public final class TestXSSFSheet extends BaseTestXSheet { checkColumnGroup(cols.getColArray(3), 10, 11); // false, true checkColumnGroup(cols.getColArray(4), 12, 12, false, false); checkColumnGroup(cols.getColArray(5), 13, 13, false, false); - + wb2.close(); } - + /** * Verify that column groups were created correctly after Sheet.groupColumn * @@ -575,7 +575,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals("isSetHidden", isSetHidden, col.isSetHidden()); assertEquals("isSetCollapsed", isSetCollapsed, col.isSetCollapsed()); //not necessarily set } - + /** * Verify that column groups were created correctly after Sheet.groupColumn * @@ -708,7 +708,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertFalse(sheet1.getRow(16).getCTRow().isSetHidden()); assertFalse(sheet1.getRow(18).getCTRow().isSetCollapsed()); assertFalse(sheet1.getRow(18).getCTRow().isSetHidden()); - + wb2.close(); } @@ -755,7 +755,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals(4, col.getMax()); assertEquals(33.0, col.getWidth(), 0.0); assertTrue(col.getCustomWidth()); - + workbook.close(); } @@ -801,7 +801,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { //now the span is splitted into 5 individual columns assertEquals(5, cols.sizeOfColArray()); for (int i = 0; i < 5; i++) { - assertEquals(cw[i]*256, sheet.getColumnWidth(i)); + assertEquals(cw[i]*256L, sheet.getColumnWidth(i)); assertEquals(cw[i], cols.getColArray(i).getWidth(), 0.0); } @@ -812,10 +812,10 @@ public final class TestXSSFSheet extends BaseTestXSheet { cols = sheet.getCTWorksheet().getColsArray(0); assertEquals(5, cols.sizeOfColArray()); for (int i = 0; i < 5; i++) { - assertEquals(cw[i]*256, sheet.getColumnWidth(i)); + assertEquals(cw[i]*256L, sheet.getColumnWidth(i)); assertEquals(cw[i], cols.getColArray(i).getWidth(), 0.0); } - + wb2.close(); } @@ -889,7 +889,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertFalse(sheet.isColumnHidden(3)); assertFalse(sheet.isColumnHidden(4)); assertFalse(sheet.isColumnHidden(5)); - + wb2.close(); } @@ -920,7 +920,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { //comment1 and comment2 are different objects assertNotSame(comment1, comment2); wb1.close(); - + //now test against a workbook containing cell comments XSSFWorkbook wb2 = XSSFTestDataSamples.openSampleWorkbook("WithMoreVariousData.xlsx"); sheet1 = wb2.getSheetAt(0); @@ -928,7 +928,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertNotNull(comment1); assertEquals("/xl/comments1.xml", comment1.getPackagePart().getPartName().getName()); assertSame(comment1, sheet1.getCommentsTable(true)); - + wb2.close(); } @@ -1034,7 +1034,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertTrue(nm.getCTName().getHidden()); assertEquals("_xlnm._FilterDatabase", nm.getCTName().getName()); assertEquals("'new sheet'!$A$1:$D$100", nm.getCTName().getStringValue()); - + wb.close(); } @@ -1057,7 +1057,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { sheet.protectSheet(null); assertNull("protectSheet(null) should unset CTSheetProtection", sheet.getCTWorksheet().getSheetProtection()); - + wb.close(); } @@ -1095,12 +1095,12 @@ public final class TestXSSFSheet extends BaseTestXSheet { wb1.close(); assertTrue(wb2.getSheetAt(0).validateSheetPassword(password)); wb2.close(); - + XSSFWorkbook wb3 = openSampleWorkbook("workbookProtection-sheet_password-2013.xlsx"); assertTrue(wb3.getSheetAt(0).validateSheetPassword("pwd")); wb3.close(); } - + @Test public void bug49966() throws IOException { @@ -1161,7 +1161,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { XSSFSheet sheet = wb1.createSheet("Sheet 1"); assertFalse(sheet.getForceFormulaRecalculation()); - + // Set sheet.setForceFormulaRecalculation(true); assertTrue(sheet.getForceFormulaRecalculation()); @@ -1221,7 +1221,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { } swb.close(); } - + wb.close(); } @@ -1246,7 +1246,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { } swb.close(); } - + wb.close(); } @@ -1295,7 +1295,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { name = wb.getBuiltInName(XSSFName.BUILTIN_FILTER_DB, 0); assertNotNull(name); assertEquals("Sheet0!$B:$C", name.getRefersToFormula()); - + wb.close(); } @@ -1389,7 +1389,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { XSSFPivotTable pivotTable = sheet2.createPivotTable (wb.getCreationHelper().createAreaReference("A1:B2"), new CellReference("H5"), sheet1); assertEquals(0, pivotTable.getRowLabelColumns().size()); - + assertEquals(1, wb.getPivotTables().size()); assertEquals(0, sheet1.getPivotTables().size()); assertEquals(1, sheet2.getPivotTables().size()); @@ -1421,12 +1421,12 @@ public final class TestXSSFSheet extends BaseTestXSheet { sheet2); wb.close(); } - + @Test(expected=POIXMLException.class) public void testReadFails() throws IOException { XSSFWorkbook wb = new XSSFWorkbook(); XSSFSheet sheet = wb.createSheet(); - + // Throws exception because we cannot read here try { sheet.onDocumentRead(); @@ -1434,8 +1434,8 @@ public final class TestXSSFSheet extends BaseTestXSheet { wb.close(); } } - - /** + + /** * This would be better off as a testable example rather than a simple unit test * since Sheet.createComment() was deprecated and removed. * https://poi.apache.org/spreadsheet/quick-guide.html#CellComments @@ -1450,7 +1450,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertNotNull(comment); wb.close(); } - + protected void testCopyOneRow(String copyRowsTestWorkbook) throws IOException { final double FLOAT_PRECISION = 1e-9; final XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook(copyRowsTestWorkbook); @@ -1496,24 +1496,24 @@ public final class TestXSSFSheet extends BaseTestXSheet { cell = CellUtil.getCell(destRow, col++); assertEquals("[String] G7 cell type", CellType.STRING, cell.getCellType()); assertEquals("[String] G7 cell value", "Hello", cell.getStringCellValue()); - + // Int cell = CellUtil.getCell(destRow, col++); assertEquals("[Int] H7 cell type", CellType.NUMERIC, cell.getCellType()); assertEquals("[Int] H7 cell value", 15, (int) cell.getNumericCellValue()); - + // Float cell = CellUtil.getCell(destRow, col++); assertEquals("[Float] I7 cell type", CellType.NUMERIC, cell.getCellType()); assertEquals("[Float] I7 cell value", 12.5, cell.getNumericCellValue(), FLOAT_PRECISION); - + // Cell Formula cell = CellUtil.getCell(destRow, col++); assertEquals("J7", new CellReference(cell).formatAsString()); assertEquals("[Cell Formula] J7 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula] J7 cell formula", "5+2", cell.getCellFormula()); //System.out.println("Cell formula evaluation currently unsupported"); - + // Cell Formula with Reference // Formula row references should be adjusted by destRowNum-srcRowNum cell = CellUtil.getCell(destRow, col++); @@ -1522,21 +1522,21 @@ public final class TestXSSFSheet extends BaseTestXSheet { CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula with Reference] K7 cell formula", "J7+H$2", cell.getCellFormula()); - + // Cell Formula with Reference spanning multiple rows cell = CellUtil.getCell(destRow, col++); assertEquals("[Cell Formula with Reference spanning multiple rows] L7 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula with Reference spanning multiple rows] L7 cell formula", "G7&\" \"&G8", cell.getCellFormula()); - + // Cell Formula with Reference spanning multiple rows cell = CellUtil.getCell(destRow, col++); assertEquals("[Cell Formula with Area Reference] M7 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula with Area Reference] M7 cell formula", "SUM(H7:I8)", cell.getCellFormula()); - + // Array Formula cell = CellUtil.getCell(destRow, col++); //System.out.println("Array formulas currently unsupported"); @@ -1545,7 +1545,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals("[Array Formula] N7 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Array Formula] N7 cell formula", "{SUM(H7:J7*{1,2,3})}", cell.getCellFormula()); */ - + // Data Format cell = CellUtil.getCell(destRow, col++); assertEquals("[Data Format] O7 cell type;", CellType.NUMERIC, cell.getCellType()); @@ -1553,14 +1553,14 @@ public final class TestXSSFSheet extends BaseTestXSheet { //FIXME: currently fails final String moneyFormat = "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)"; assertEquals("[Data Format] O7 data format", moneyFormat, cell.getCellStyle().getDataFormatString()); - + // Merged cell = CellUtil.getCell(destRow, col); assertEquals("[Merged] P7:Q7 cell value", "Merged cells", cell.getStringCellValue()); assertTrue("[Merged] P7:Q7 merged region", sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P7:Q7"))); - + // Merged across multiple rows // Microsoft Excel 2013 does not copy a merged region unless all rows of // the source merged region are selected @@ -1571,23 +1571,23 @@ public final class TestXSSFSheet extends BaseTestXSheet { // which will not overwrite a cell in destination row if merged region extends beyond the copied row. // The Excel way would require: //assertEquals("[Merged across multiple rows] R7:S8 merged region", "Should NOT be overwritten", cell.getStringCellValue()); - //assertFalse("[Merged across multiple rows] R7:S8 merged region", + //assertFalse("[Merged across multiple rows] R7:S8 merged region", // sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S8"))); // As currently implemented, cell value is copied but merged region is not copied assertEquals("[Merged across multiple rows] R7:S8 cell value", "Merged cells across multiple rows", cell.getStringCellValue()); - assertFalse("[Merged across multiple rows] R7:S7 merged region (one row)", + assertFalse("[Merged across multiple rows] R7:S7 merged region (one row)", sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S7"))); //shouldn't do 1-row merge - assertFalse("[Merged across multiple rows] R7:S8 merged region", + assertFalse("[Merged across multiple rows] R7:S8 merged region", sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R7:S8"))); //shouldn't do 2-row merge - + // Make sure other rows are blank (off-by-one errors) assertNull(sheet.getRow(5)); assertNull(sheet.getRow(7)); - + wb.close(); } - + protected void testCopyMultipleRows(String copyRowsTestWorkbook) throws IOException { final double FLOAT_PRECISION = 1e-9; final XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook(copyRowsTestWorkbook); @@ -1595,8 +1595,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { final CellCopyPolicy defaultCopyPolicy = new CellCopyPolicy(); sheet.copyRows(0, 3, 8, defaultCopyPolicy); - @SuppressWarnings("unused") - final Row srcHeaderRow = sheet.getRow(0); + sheet.getRow(0); final Row srcRow1 = sheet.getRow(1); final Row srcRow2 = sheet.getRow(2); final Row srcRow3 = sheet.getRow(3); @@ -1606,102 +1605,102 @@ public final class TestXSSFSheet extends BaseTestXSheet { final Row destRow3 = sheet.getRow(11); int col = 0; Cell cell; - + // Header row should be copied assertNotNull(destHeaderRow); - + // Data rows cell = CellUtil.getCell(destRow1, col); assertEquals("Source row ->", cell.getStringCellValue()); - + // Style col++; cell = CellUtil.getCell(destRow1, col); assertEquals("[Style] B10 cell value", "Red", cell.getStringCellValue()); assertEquals("[Style] B10 cell style", CellUtil.getCell(srcRow1, 1).getCellStyle(), cell.getCellStyle()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Style] B11 cell value", "Blue", cell.getStringCellValue()); assertEquals("[Style] B11 cell style", CellUtil.getCell(srcRow2, 1).getCellStyle(), cell.getCellStyle()); - + // Blank col++; cell = CellUtil.getCell(destRow1, col); assertEquals("[Blank] C10 cell type", CellType.BLANK, cell.getCellType()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Blank] C11 cell type", CellType.BLANK, cell.getCellType()); - + // Error col++; cell = CellUtil.getCell(destRow1, col); assertEquals("[Error] D10 cell type", CellType.ERROR, cell.getCellType()); FormulaError error = FormulaError.forInt(cell.getErrorCellValue()); assertEquals("[Error] D10 cell value", FormulaError.NA, error); //FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Error] D11 cell type", CellType.ERROR, cell.getCellType()); error = FormulaError.forInt(cell.getErrorCellValue()); assertEquals("[Error] D11 cell value", FormulaError.NAME, error); //FIXME: XSSFCell and HSSFCell expose different interfaces. getErrorCellString would be helpful here - + // Date col++; cell = CellUtil.getCell(destRow1, col); assertEquals("[Date] E10 cell type", CellType.NUMERIC, cell.getCellType()); Date date = LocaleUtil.getLocaleCalendar(2000, Calendar.JANUARY, 1).getTime(); assertEquals("[Date] E10 cell value", date, cell.getDateCellValue()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Date] E11 cell type", CellType.NUMERIC, cell.getCellType()); date = LocaleUtil.getLocaleCalendar(2000, Calendar.JANUARY, 2).getTime(); assertEquals("[Date] E11 cell value", date, cell.getDateCellValue()); - + // Boolean col++; cell = CellUtil.getCell(destRow1, col); assertEquals("[Boolean] F10 cell type", CellType.BOOLEAN, cell.getCellType()); assertEquals("[Boolean] F10 cell value", true, cell.getBooleanCellValue()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Boolean] F11 cell type", CellType.BOOLEAN, cell.getCellType()); assertEquals("[Boolean] F11 cell value", false, cell.getBooleanCellValue()); - + // String col++; cell = CellUtil.getCell(destRow1, col); assertEquals("[String] G10 cell type", CellType.STRING, cell.getCellType()); assertEquals("[String] G10 cell value", "Hello", cell.getStringCellValue()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[String] G11 cell type", CellType.STRING, cell.getCellType()); assertEquals("[String] G11 cell value", "World", cell.getStringCellValue()); - + // Int col++; cell = CellUtil.getCell(destRow1, col); assertEquals("[Int] H10 cell type", CellType.NUMERIC, cell.getCellType()); assertEquals("[Int] H10 cell value", 15, (int) cell.getNumericCellValue()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Int] H11 cell type", CellType.NUMERIC, cell.getCellType()); assertEquals("[Int] H11 cell value", 42, (int) cell.getNumericCellValue()); - + // Float col++; cell = CellUtil.getCell(destRow1, col); assertEquals("[Float] I10 cell type", CellType.NUMERIC, cell.getCellType()); assertEquals("[Float] I10 cell value", 12.5, cell.getNumericCellValue(), FLOAT_PRECISION); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Float] I11 cell type", CellType.NUMERIC, cell.getCellType()); assertEquals("[Float] I11 cell value", 5.5, cell.getNumericCellValue(), FLOAT_PRECISION); - + // Cell Formula col++; cell = CellUtil.getCell(destRow1, col); assertEquals("[Cell Formula] J10 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula] J10 cell formula", "5+2", cell.getCellFormula()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Cell Formula] J11 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula] J11 cell formula", "6+18", cell.getCellFormula()); @@ -1714,11 +1713,11 @@ public final class TestXSSFSheet extends BaseTestXSheet { CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula with Reference] K10 cell formula", "J10+H$2", cell.getCellFormula()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Cell Formula with Reference] K11 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula with Reference] K11 cell formula", "J11+H$2", cell.getCellFormula()); - + // Cell Formula with Reference spanning multiple rows col++; cell = CellUtil.getCell(destRow1, col); @@ -1726,13 +1725,13 @@ public final class TestXSSFSheet extends BaseTestXSheet { CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula with Reference spanning multiple rows] L10 cell formula", "G10&\" \"&G11", cell.getCellFormula()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Cell Formula with Reference spanning multiple rows] L11 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula with Reference spanning multiple rows] L11 cell formula", "G11&\" \"&G12", cell.getCellFormula()); - + // Cell Formula with Area Reference col++; cell = CellUtil.getCell(destRow1, col); @@ -1740,13 +1739,13 @@ public final class TestXSSFSheet extends BaseTestXSheet { CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula with Area Reference] M10 cell formula", "SUM(H10:I11)", cell.getCellFormula()); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Cell Formula with Area Reference] M11 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Cell Formula with Area Reference] M11 cell formula", "SUM($H$3:I10)", cell.getCellFormula()); //Also acceptable: SUM($H10:I$3), but this AreaReference isn't in ascending order - + // Array Formula col++; cell = CellUtil.getCell(destRow1, col); @@ -1755,13 +1754,13 @@ public final class TestXSSFSheet extends BaseTestXSheet { // FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() assertEquals("[Array Formula] N10 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Array Formula] N10 cell formula", "{SUM(H10:J10*{1,2,3})}", cell.getCellFormula()); - + cell = CellUtil.getCell(destRow2, col); - // FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() + // FIXME: Array Formula set with Sheet.setArrayFormula() instead of cell.setFormula() assertEquals("[Array Formula] N11 cell type", CellType.FORMULA, cell.getCellType()); assertEquals("[Array Formula] N11 cell formula", "{SUM(H11:J11*{1,2,3})}", cell.getCellFormula()); */ - + // Data Format col++; cell = CellUtil.getCell(destRow2, col); @@ -1769,7 +1768,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals("[Data Format] O10 cell value", 100.20, cell.getNumericCellValue(), FLOAT_PRECISION); final String moneyFormat = "\"$\"#,##0.00_);[Red]\\(\"$\"#,##0.00\\)"; assertEquals("[Data Format] O10 cell data format", moneyFormat, cell.getCellStyle().getDataFormatString()); - + // Merged col++; cell = CellUtil.getCell(destRow1, col); @@ -1777,42 +1776,42 @@ public final class TestXSSFSheet extends BaseTestXSheet { "Merged cells", cell.getStringCellValue()); assertTrue("[Merged] P10:Q10 merged region", sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P10:Q10"))); - + cell = CellUtil.getCell(destRow2, col); assertEquals("[Merged] P11:Q11 cell value", "Merged cells", cell.getStringCellValue()); assertTrue("[Merged] P11:Q11 merged region", sheet.getMergedRegions().contains(CellRangeAddress.valueOf("P11:Q11"))); - + // Should Q10/Q11 be checked? - + // Merged across multiple rows // Microsoft Excel 2013 does not copy a merged region unless all rows of // the source merged region are selected // POI's behavior should match this behavior col += 2; cell = CellUtil.getCell(destRow1, col); - assertEquals("[Merged across multiple rows] R10:S11 cell value", + assertEquals("[Merged across multiple rows] R10:S11 cell value", "Merged cells across multiple rows", cell.getStringCellValue()); - assertTrue("[Merged across multiple rows] R10:S11 merged region", + assertTrue("[Merged across multiple rows] R10:S11 merged region", sheet.getMergedRegions().contains(CellRangeAddress.valueOf("R10:S11"))); - + // Row 3 (zero-based) was empty, so Row 11 (zero-based) should be empty too. if (srcRow3 == null) { assertNull("Row 3 was empty, so Row 11 should be empty", destRow3); } - + // Make sure other rows are blank (off-by-one errors) assertNull("Off-by-one lower edge case", sheet.getRow(7)); //one row above destHeaderRow assertNull("Off-by-one upper edge case", sheet.getRow(12)); //one row below destRow3 - + wb.close(); } - + @Test public void testCopyOneRow() throws IOException { testCopyOneRow("XSSFSheet.copyRows.xlsx"); } - + @Test public void testCopyMultipleRows() throws IOException { testCopyMultipleRows("XSSFSheet.copyRows.xlsx"); @@ -1828,12 +1827,12 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals(1, ignoredError.getSqref().size()); assertEquals("B2:D4", ignoredError.getSqref().get(0)); assertTrue(ignoredError.getNumberStoredAsText()); - + Map<IgnoredErrorType, Set<CellRangeAddress>> ignoredErrors = sheet.getIgnoredErrors(); assertEquals(1, ignoredErrors.size()); assertEquals(1, ignoredErrors.get(IgnoredErrorType.NUMBER_STORED_AS_TEXT).size()); assertEquals("B2:D4", ignoredErrors.get(IgnoredErrorType.NUMBER_STORED_AS_TEXT).iterator().next().formatAsString()); - + workbook.close(); } @@ -1849,7 +1848,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertFalse(ignoredError.getNumberStoredAsText()); assertTrue(ignoredError.getFormula()); assertTrue(ignoredError.getEvalError()); - + Map<IgnoredErrorType, Set<CellRangeAddress>> ignoredErrors = sheet.getIgnoredErrors(); assertEquals(2, ignoredErrors.size()); assertEquals(1, ignoredErrors.get(IgnoredErrorType.FORMULA).size()); @@ -1867,19 +1866,19 @@ public final class TestXSSFSheet extends BaseTestXSheet { // Two calls means two elements, no clever collapsing just yet. sheet.addIgnoredErrors(region, IgnoredErrorType.EVALUATION_ERROR); sheet.addIgnoredErrors(region, IgnoredErrorType.FORMULA); - + CTIgnoredError ignoredError = sheet.getCTWorksheet().getIgnoredErrors().getIgnoredErrorArray(0); assertEquals(1, ignoredError.getSqref().size()); assertEquals("B2:D4", ignoredError.getSqref().get(0)); assertFalse(ignoredError.getFormula()); assertTrue(ignoredError.getEvalError()); - + ignoredError = sheet.getCTWorksheet().getIgnoredErrors().getIgnoredErrorArray(1); assertEquals(1, ignoredError.getSqref().size()); assertEquals("B2:D4", ignoredError.getSqref().get(0)); assertTrue(ignoredError.getFormula()); assertFalse(ignoredError.getEvalError()); - + Map<IgnoredErrorType, Set<CellRangeAddress>> ignoredErrors = sheet.getIgnoredErrors(); assertEquals(2, ignoredErrors.size()); assertEquals(1, ignoredErrors.get(IgnoredErrorType.FORMULA).size()); @@ -1888,7 +1887,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals("B2:D4", ignoredErrors.get(IgnoredErrorType.EVALUATION_ERROR).iterator().next().formatAsString()); workbook.close(); } - + @Test public void setTabColor() throws IOException { try (XSSFWorkbook wb = new XSSFWorkbook()) { @@ -1900,7 +1899,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { sh.getCTWorksheet().getSheetPr().getTabColor().getIndexed()); } } - + @Test public void getTabColor() throws IOException { try (XSSFWorkbook wb = new XSSFWorkbook()) { @@ -1912,7 +1911,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals(expected, sh.getTabColor()); } } - + // Test using an existing workbook saved by Excel @Test public void tabColor() throws IOException { @@ -1930,7 +1929,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { assertEquals(expected, wb.getSheet("customOrange").getTabColor()); } } - + /** * See bug #52425 */ @@ -1947,7 +1946,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { // Adding Comment to cloned Sheet 3 addComments(helper, sheet3); } - + private void addComments(CreationHelper helper, Sheet sheet) { Drawing<?> drawing = sheet.createDrawingPatriarch(); @@ -1974,7 +1973,7 @@ public final class TestXSSFSheet extends BaseTestXSheet { } } - + // bug 59687: XSSFSheet.RemoveRow doesn't handle row gaps properly when removing row comments @Test public void testRemoveRowWithCommentAndGapAbove() throws IOException { @@ -1984,22 +1983,22 @@ public final class TestXSSFSheet extends BaseTestXSheet { // comment exists CellAddress commentCellAddress = new CellAddress("A4"); assertNotNull(sheet.getCellComment(commentCellAddress)); - + assertEquals("Wrong starting # of comments", 1, sheet.getCellComments().size()); - + sheet.removeRow(sheet.getRow(commentCellAddress.getRow())); - + assertEquals("There should not be any comments left!", 0, sheet.getCellComments().size()); } - + @Test public void testGetHeaderFooterProperties() throws IOException { XSSFWorkbook wb = new XSSFWorkbook(); XSSFSheet sh = wb.createSheet(); - + XSSFHeaderFooterProperties hfProp = sh.getHeaderFooterProperties(); assertNotNull(hfProp); - + wb.close(); } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java index 20dad4b84a..3fa007ba3e 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java @@ -1044,8 +1044,10 @@ public final class TestXSSFWorkbook extends BaseTestXWorkbook { //assertCloseDoesNotModifyFile(filename, wb); // InputStream - wb = new XSSFWorkbook(new FileInputStream(file)); - assertCloseDoesNotModifyFile(filename, wb); + try (FileInputStream is = new FileInputStream(file)) { + wb = new XSSFWorkbook(is); + assertCloseDoesNotModifyFile(filename, wb); + } // OPCPackage //wb = new XSSFWorkbook(OPCPackage.open(file)); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java index 14f467a37f..f85f44d22a 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/charts/TestXSSFChartTitle.java @@ -65,7 +65,7 @@ public class TestXSSFChartTitle { row = sheet.createRow((short) rowIndex); for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) { cell = row.createCell((short) colIndex); - cell.setCellValue(colIndex * (rowIndex + 1)); + cell.setCellValue(colIndex * (rowIndex + 1L)); } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/helpers/TestColumnHelper.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/helpers/TestColumnHelper.java index 5ba7ad1d03..9686dcf6a1 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/helpers/TestColumnHelper.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/helpers/TestColumnHelper.java @@ -25,6 +25,9 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; +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.model.StylesTable; import org.apache.poi.xssf.usermodel.XSSFCellStyle; import org.apache.poi.xssf.usermodel.XSSFSheet; @@ -192,14 +195,14 @@ public final class TestColumnHelper { } @Test - public void testAddCleanColIntoColsExactOverlap() throws Exception { + public void testAddCleanColIntoColsExactOverlap() { CTCols cols = createHiddenAndBestFitColsWithHelper(1, 1, 1, 1); assertEquals(1, cols.sizeOfColArray()); assertMinMaxHiddenBestFit(cols, 0, 1, 1, true, true); } @Test - public void testAddCleanColIntoColsOverlapsOverhangingBothSides() throws Exception { + public void testAddCleanColIntoColsOverlapsOverhangingBothSides() { CTCols cols = createHiddenAndBestFitColsWithHelper(2, 2, 1, 3); assertEquals(3, cols.sizeOfColArray()); assertMinMaxHiddenBestFit(cols, 0, 1, 1, false, true); @@ -208,7 +211,7 @@ public final class TestColumnHelper { } @Test - public void testAddCleanColIntoColsOverlapsCompletelyNested() throws Exception { + public void testAddCleanColIntoColsOverlapsCompletelyNested() { CTCols cols = createHiddenAndBestFitColsWithHelper(1, 3, 2, 2); assertEquals(3, cols.sizeOfColArray()); assertMinMaxHiddenBestFit(cols, 0, 1, 1, true, false); @@ -217,7 +220,7 @@ public final class TestColumnHelper { } @Test - public void testAddCleanColIntoColsNewOverlapsOverhangingLeftNotRightExactRight() throws Exception { + public void testAddCleanColIntoColsNewOverlapsOverhangingLeftNotRightExactRight() { CTCols cols = createHiddenAndBestFitColsWithHelper(2, 3, 1, 3); assertEquals(2, cols.sizeOfColArray()); assertMinMaxHiddenBestFit(cols, 0, 1, 1, false, true); @@ -225,7 +228,7 @@ public final class TestColumnHelper { } @Test - public void testAddCleanColIntoColsNewOverlapsOverhangingRightNotLeftExactLeft() throws Exception { + public void testAddCleanColIntoColsNewOverlapsOverhangingRightNotLeftExactLeft() { CTCols cols = createHiddenAndBestFitColsWithHelper(1, 2, 1, 3); assertEquals(2, cols.sizeOfColArray()); assertMinMaxHiddenBestFit(cols, 0, 1, 2, true, true); @@ -233,7 +236,7 @@ public final class TestColumnHelper { } @Test - public void testAddCleanColIntoColsNewOverlapsOverhangingLeftNotRight() throws Exception { + public void testAddCleanColIntoColsNewOverlapsOverhangingLeftNotRight() { CTCols cols = createHiddenAndBestFitColsWithHelper(2, 3, 1, 2); assertEquals(3, cols.sizeOfColArray()); assertMinMaxHiddenBestFit(cols, 0, 1, 1, false, true); @@ -242,7 +245,7 @@ public final class TestColumnHelper { } @Test - public void testAddCleanColIntoColsNewOverlapsOverhangingRightNotLeft() throws Exception { + public void testAddCleanColIntoColsNewOverlapsOverhangingRightNotLeft() { CTCols cols = createHiddenAndBestFitColsWithHelper(1, 2, 2, 3); assertEquals(3, cols.sizeOfColArray()); assertMinMaxHiddenBestFit(cols, 0, 1, 1, true, false); @@ -402,4 +405,36 @@ public final class TestColumnHelper { } return count; } + + @SuppressWarnings("deprecation") + @Test + public void testColumnsCollapsed() { + Workbook wb = new XSSFWorkbook(); + Sheet sheet = wb.createSheet("test"); + Row row = sheet.createRow(0); + row.createCell(0); + row.createCell(1); + row.createCell(2); + + sheet.setColumnWidth(0, 10); + sheet.setColumnWidth(1, 10); + sheet.setColumnWidth(2, 10); + + sheet.groupColumn(0, 1); + sheet.setColumnGroupCollapsed(0, true); + + CTCols ctCols = ((XSSFSheet) sheet).getCTWorksheet().getColsArray()[0]; + assertEquals(3, ctCols.sizeOfColArray()); + assertTrue(ctCols.getColArray(0).isSetCollapsed()); + assertTrue(ctCols.getColArray(1).isSetCollapsed()); + assertTrue(ctCols.getColArray(2).isSetCollapsed()); + + ColumnHelper helper = new ColumnHelper(CTWorksheet.Factory.newInstance()); + helper.setColumnAttributes(ctCols.getColArray(1), ctCols.getColArray(2)); + + ctCols = ((XSSFSheet) sheet).getCTWorksheet().getColsArray()[0]; + assertTrue(ctCols.getColArray(0).isSetCollapsed()); + assertTrue(ctCols.getColArray(1).isSetCollapsed()); + assertTrue(ctCols.getColArray(2).isSetCollapsed()); + } } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/util/MemoryUsage.java b/src/ooxml/testcases/org/apache/poi/xssf/util/MemoryUsage.java index f13337fa94..99bf9f4c8b 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/util/MemoryUsage.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/util/MemoryUsage.java @@ -17,18 +17,23 @@ package org.apache.poi.xssf.util; -import junit.framework.TestCase; +import java.util.ArrayList; +import java.util.List; + import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.usermodel.Sheet; -import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Cell; +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.ss.util.CellReference; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetData; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType; -import java.util.List; -import java.util.ArrayList; +import junit.framework.TestCase; /** * Mixed utilities for testing memory usage in XSSF @@ -37,7 +42,7 @@ import java.util.ArrayList; */ public class MemoryUsage extends TestCase { private static final int NUM_COLUMNS = 255; - + private static void printMemoryUsage(String msg) { System.out.println(" Memory (" + msg + "): " + Runtime.getRuntime().totalMemory()/(1024*1024) + "MB"); } @@ -62,8 +67,11 @@ public class MemoryUsage extends TestCase { Row row = sh.createRow(i); for(int j=0; j < numCols; j++){ Cell cell = row.createCell(j); - if(j % 2 == 0) cell.setCellValue(j); - else cell.setCellValue(new CellReference(j, i).formatAsString()); + if(j % 2 == 0) { + cell.setCellValue(j); + } else { + cell.setCellValue(new CellReference(j, i).formatAsString()); + } cnt++; } } @@ -78,7 +86,7 @@ public class MemoryUsage extends TestCase { /** * Generate a spreadsheet who's all cell values are numbers. - * The data is generated until OutOfMemoryError. + * The data is generated until OutOfMemoryError. * <p> * as compared to {@link #mixedSpreadsheet(org.apache.poi.ss.usermodel.Workbook, int)}, * this method does not set string values and, hence, does not involve the Shared Strings Table. @@ -161,7 +169,7 @@ public class MemoryUsage extends TestCase { rows.add(r); } } catch (OutOfMemoryError er) { - System.out.println("Failed at row=" + i); + System.out.println("Failed at row=" + i + " from " + rows.size() + " kept."); } catch (final Exception e) { System.out.println("Unable to reach an OutOfMemoryError"); System.out.println(e.getClass().getName() + ": " + e.getMessage()); @@ -190,7 +198,7 @@ public class MemoryUsage extends TestCase { rows.add(r); } } catch (OutOfMemoryError er) { - System.out.println("Failed at row=" + i); + System.out.println("Failed at row=" + i + " from " + rows.size() + " kept."); } catch (final Exception e) { System.out.println("Unable to reach an OutOfMemoryError"); System.out.println(e.getClass().getName() + ": " + e.getMessage()); diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java index d51750e922..0fcccd1838 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java @@ -29,6 +29,7 @@ import org.apache.poi.xwpf.XWPFTestDataSamples; import org.apache.poi.xwpf.usermodel.XWPFDocument; import static org.apache.poi.POITestCase.assertContains; import static org.apache.poi.POITestCase.assertEndsWith; +import static org.apache.poi.POITestCase.assertNotContained; import static org.apache.poi.POITestCase.assertStartsWith; /** @@ -441,4 +442,14 @@ public class TestXWPFWordExtractor extends TestCase { assertContains(txt, "Sequencing data"); extractor.close(); } + + public void testGlossary() throws IOException { + XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("60316.dotx"); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc); + String txt = extractor.getText(); + assertContains(txt, "Getting the perfect"); + //this content appears only in the glossary document + //once we add processing for this, we can change this to contains + assertNotContained(txt, "table rows"); + } } diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java index deac5c09ae..9388e08710 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/usermodel/TestXWPFSDT.java @@ -148,6 +148,19 @@ public final class TestXWPFSDT { assertEquals("", sdts.get(0).getTitle()); } + @Test + public void test62859() throws IOException { + //this doesn't test the exact code path for this issue, but + //it does test for a related issue, and the fix fixes both. + //We should try to add the actual triggering document + //to our test suite. + XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("Bug62859.docx"); + List<XWPFAbstractSDT> sdts = extractAllSDTs(doc); + assertEquals(1, sdts.size()); + assertEquals("", sdts.get(0).getTag()); + assertEquals("", sdts.get(0).getTitle()); + } + private List<XWPFAbstractSDT> extractAllSDTs(XWPFDocument doc) { List<XWPFAbstractSDT> sdts = new ArrayList<>(); diff --git a/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java b/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java index 8eec2af3fe..0c8bcf5003 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/dev/HWPFLister.java @@ -75,33 +75,25 @@ import org.apache.poi.util.LittleEndian; * @author Sergey Vladimirov (vlsergey at gmail dot com) */ @Beta -public final class HWPFLister -{ - private static HWPFDocumentCore loadDoc( File docFile ) throws IOException - { +public final class HWPFLister { + private static HWPFDocumentCore loadDoc( File docFile ) throws IOException { try (final FileInputStream istream = new FileInputStream( docFile )) { return loadDoc( istream ); } } private static HWPFDocumentCore loadDoc( InputStream inputStream ) - throws IOException - { - final POIFSFileSystem poifsFileSystem = HWPFDocumentCore.verifyAndBuildPOIFS( inputStream ); - try - { + throws IOException { + final POIFSFileSystem poifsFileSystem = HWPFDocumentCore.verifyAndBuildPOIFS( inputStream ); // NOSONAR + try { return new HWPFDocument( poifsFileSystem ); - } - catch ( OldWordFileFormatException exc ) - { + } catch ( OldWordFileFormatException exc ) { return new HWPFOldDocument( poifsFileSystem ); } } - public static void main( String[] args ) throws Exception - { - if ( args.length == 0 ) - { + public static void main( String[] args ) throws Exception { + if ( args.length == 0 ) { System.err.println( "Use:" ); System.err.println( "\tHWPFLister <filename>\n" + "\t\t[--dop]\n" + "\t\t[--textPieces] [--textPiecesText]\n" @@ -140,8 +132,7 @@ public final class HWPFLister boolean writereadback = false; - for ( String arg : Arrays.asList( args ).subList( 1, args.length ) ) - { + for ( String arg : Arrays.asList( args ).subList( 1, args.length ) ) { if ( "--dop".equals( arg ) ) outputDop = true; @@ -190,17 +181,14 @@ public final class HWPFLister if ( writereadback ) doc = writeOutAndReadBack( doc ); - HWPFDocumentCore original; - { - System.setProperty( "org.apache.poi.hwpf.preserveBinTables", - Boolean.TRUE.toString() ); - System.setProperty( "org.apache.poi.hwpf.preserveTextTable", - Boolean.TRUE.toString() ); + System.setProperty( "org.apache.poi.hwpf.preserveBinTables", + Boolean.TRUE.toString() ); + System.setProperty( "org.apache.poi.hwpf.preserveTextTable", + Boolean.TRUE.toString() ); - original = loadDoc( new File( args[0] ) ); - if ( writereadback ) - original = writeOutAndReadBack( original ); - } + HWPFDocumentCore original = loadDoc( new File( args[0] ) ); + if ( writereadback ) + original = writeOutAndReadBack( original ); HWPFLister listerOriginal = new HWPFLister( original ); HWPFLister listerRebuilded = new HWPFLister( doc ); @@ -211,20 +199,17 @@ public final class HWPFLister System.out.println( "== FIB (original) ==" ); listerOriginal.dumpFIB(); - if ( outputDop ) - { + if ( outputDop ) { System.out.println( "== Document properties ==" ); listerOriginal.dumpDop(); } - if ( outputTextPieces ) - { + if ( outputTextPieces ) { System.out.println( "== Text pieces (original) ==" ); listerOriginal.dumpTextPieces( outputTextPiecesText ); } - if ( outputChpx ) - { + if ( outputChpx ) { System.out.println( "== CHPX (original) ==" ); listerOriginal.dumpChpx( outputChpxProperties, outputChpxSprms ); @@ -232,8 +217,7 @@ public final class HWPFLister listerRebuilded.dumpChpx( outputChpxProperties, outputChpxSprms ); } - if ( outputPapx ) - { + if ( outputPapx ) { System.out.println( "== PAPX (original) ==" ); listerOriginal.dumpPapx( outputPapxProperties, outputPapxSprms ); @@ -241,8 +225,7 @@ public final class HWPFLister listerRebuilded.dumpPapx( outputPapxProperties, outputPapxSprms ); } - if ( outputParagraphs ) - { + if ( outputParagraphs ) { System.out.println( "== Text paragraphs (original) ==" ); listerRebuilded.dumpParagraphs( true ); @@ -250,56 +233,47 @@ public final class HWPFLister listerRebuilded.dumpParagraphsDom( outputParagraphsText ); } - if ( outputBookmarks ) - { + if ( outputBookmarks ) { System.out.println( "== BOOKMARKS (rebuilded) ==" ); listerRebuilded.dumpBookmarks(); } - if ( outputEscher ) - { + if ( outputEscher ) { System.out.println( "== ESCHER PROPERTIES (rebuilded) ==" ); listerRebuilded.dumpEscher(); } - if ( outputFields ) - { + if ( outputFields ) { System.out.println( "== FIELDS (rebuilded) ==" ); listerRebuilded.dumpFields(); } - if ( outputOfficeDrawings ) - { + if ( outputOfficeDrawings ) { System.out.println( "== OFFICE DRAWINGS (rebuilded) ==" ); listerRebuilded.dumpOfficeDrawings(); } - if ( outputPictures ) - { + if ( outputPictures ) { System.out.println( "== PICTURES (rebuilded) ==" ); listerRebuilded.dumpPictures(); } - if ( outputStyles ) - { + if ( outputStyles ) { System.out.println( "== STYLES (rebuilded) ==" ); listerRebuilded.dumpStyles(); } } private static HWPFDocumentCore writeOutAndReadBack( - HWPFDocumentCore original ) - { - try - { + HWPFDocumentCore original ) { + try { ByteArrayOutputStream baos = new ByteArrayOutputStream( 4096 ); original.write( baos ); ByteArrayInputStream bais = new ByteArrayInputStream( baos.toByteArray() ); return loadDoc( bais ); } - catch ( IOException e ) - { + catch ( IOException e ) { throw new RuntimeException( e ); } } @@ -308,82 +282,67 @@ public final class HWPFLister private LinkedHashMap<Integer, String> paragraphs; - public HWPFLister( HWPFDocumentCore doc ) - { + public HWPFLister( HWPFDocumentCore doc ) { _doc = doc; buildParagraphs(); } - private void buildParagraphs() - { + private void buildParagraphs() { paragraphs = new LinkedHashMap<>(); StringBuilder part = new StringBuilder(); String text = _doc.getDocumentText(); - for ( int charIndex = 0; charIndex < text.length(); charIndex++ ) - { + for ( int charIndex = 0; charIndex < text.length(); charIndex++ ) { char c = text.charAt( charIndex ); part.append( c ); - if ( c == 13 || c == 7 || c == 12 ) - { + if ( c == 13 || c == 7 || c == 12 ) { paragraphs.put( Integer.valueOf( charIndex ), part.toString() ); part.setLength( 0 ); } } } - private void dumpBookmarks() - { - if ( !( _doc instanceof HWPFDocument ) ) - { + private void dumpBookmarks() { + if ( !( _doc instanceof HWPFDocument ) ) { System.out.println( "Word 95 not supported so far" ); return; } HWPFDocument document = (HWPFDocument) _doc; Bookmarks bookmarks = document.getBookmarks(); - for ( int b = 0; b < bookmarks.getBookmarksCount(); b++ ) - { + for ( int b = 0; b < bookmarks.getBookmarksCount(); b++ ) { Bookmark bookmark = bookmarks.getBookmark( b ); System.out.println( "[" + bookmark.getStart() + "; " + bookmark.getEnd() + "): " + bookmark.getName() ); } } - public void dumpChpx( boolean withProperties, boolean withSprms ) - { - for ( CHPX chpx : _doc.getCharacterTable().getTextRuns() ) - { + public void dumpChpx( boolean withProperties, boolean withSprms ) { + for ( CHPX chpx : _doc.getCharacterTable().getTextRuns() ) { System.out.println( chpx ); - if ( withProperties ) - { + if ( withProperties ) { System.out.println( chpx.getCharacterProperties( _doc.getStyleSheet(), (short) StyleSheet.NIL_STYLE ) ); } - if ( withSprms ) - { + if ( withSprms ) { SprmIterator sprmIt = new SprmIterator( chpx.getGrpprl(), 0 ); - while ( sprmIt.hasNext() ) - { + while ( sprmIt.hasNext() ) { SprmOperation sprm = sprmIt.next(); System.out.println( "\t" + sprm); } } String text = new Range( chpx.getStart(), chpx.getEnd(), - _doc.getOverallRange() ) - { - public String toString() - { + _doc.getOverallRange() ) { + public String toString() { return "CHPX range (" + super.toString() + ")"; } }.text(); StringBuilder stringBuilder = new StringBuilder(); - for ( char c : text.toCharArray() ) - { + for ( char c : text.toCharArray() ) { if ( c < 30 ) stringBuilder.append("\\0x").append(Integer.toHexString(c)); else @@ -393,10 +352,8 @@ public final class HWPFLister } } - private void dumpDop() - { - if ( !( _doc instanceof HWPFDocument ) ) - { + private void dumpDop() { + if ( !( _doc instanceof HWPFDocument ) ) { System.out.println( "Word 95 not supported so far" ); return; } @@ -404,10 +361,8 @@ public final class HWPFLister System.out.println( ( (HWPFDocument) _doc ).getDocProperties() ); } - private void dumpEscher() - { - if ( _doc instanceof HWPFOldDocument ) - { + private void dumpEscher() { + if ( _doc instanceof HWPFOldDocument ) { System.out.println( "Word 95 not supported so far" ); return; } @@ -415,46 +370,38 @@ public final class HWPFLister System.out.println( ( (HWPFDocument) _doc ).getEscherRecordHolder() ); } - public void dumpFIB() - { + public void dumpFIB() { FileInformationBlock fib = _doc.getFileInformationBlock(); System.out.println( fib ); } - private void dumpFields() - { - if ( !( _doc instanceof HWPFDocument ) ) - { + private void dumpFields() { + if ( !( _doc instanceof HWPFDocument ) ) { System.out.println( "Word 95 not supported so far" ); return; } HWPFDocument document = (HWPFDocument) _doc; - for ( FieldsDocumentPart part : FieldsDocumentPart.values() ) - { + for ( FieldsDocumentPart part : FieldsDocumentPart.values() ) { System.out.println( "=== Document part: " + part + " ===" ); - for ( Field field : document.getFields().getFields( part ) ) - { + for ( Field field : document.getFields().getFields( part ) ) { System.out.println( field ); } } } - public void dumpFileSystem() throws Exception - { + public void dumpFileSystem() throws Exception { System.out.println( dumpFileSystem( _doc.getDirectory() ) ); } - private String dumpFileSystem( DirectoryEntry directory ) - { + private String dumpFileSystem( DirectoryEntry directory ) { StringBuilder result = new StringBuilder(); result.append( "+ " ); result.append( directory.getName() ); for ( Iterator<Entry> iterator = directory.getEntries(); iterator - .hasNext(); ) - { + .hasNext(); ) { Entry entry = iterator.next(); String entryToString = "\n" + dumpFileSystem( entry ); entryToString = entryToString.replaceAll( "\n", "\n+---" ); @@ -464,50 +411,41 @@ public final class HWPFLister return result.toString(); } - private String dumpFileSystem( Entry entry ) - { + private String dumpFileSystem( Entry entry ) { if ( entry instanceof DirectoryEntry ) return dumpFileSystem( (DirectoryEntry) entry ); return entry.getName(); } - private void dumpOfficeDrawings() - { - if ( !( _doc instanceof HWPFDocument ) ) - { + private void dumpOfficeDrawings() { + if ( !( _doc instanceof HWPFDocument ) ) { System.out.println( "Word 95 not supported so far" ); return; } HWPFDocument document = (HWPFDocument) _doc; - if ( document.getOfficeDrawingsHeaders() != null ) - { + if ( document.getOfficeDrawingsHeaders() != null ) { System.out.println( "=== Document part: HEADER ===" ); for ( OfficeDrawing officeDrawing : document - .getOfficeDrawingsHeaders().getOfficeDrawings() ) - { + .getOfficeDrawingsHeaders().getOfficeDrawings() ) { System.out.println( officeDrawing ); } } - if ( document.getOfficeDrawingsHeaders() != null ) - { + if ( document.getOfficeDrawingsHeaders() != null ) { System.out.println( "=== Document part: MAIN ===" ); for ( OfficeDrawing officeDrawing : document - .getOfficeDrawingsMain().getOfficeDrawings() ) - { + .getOfficeDrawingsMain().getOfficeDrawings() ) { System.out.println( officeDrawing ); } } } public void dumpPapx( boolean withProperties, boolean withSprms ) - throws Exception - { - if ( _doc instanceof HWPFDocument ) - { + throws Exception { + if ( _doc instanceof HWPFDocument ) { System.out.println( "binary PAP pages " ); HWPFDocument doc = (HWPFDocument) _doc; @@ -521,8 +459,7 @@ public final class HWPFLister List<PAPX> papxs = new ArrayList<>(); int length = binTable.length(); - for ( int x = 0; x < length; x++ ) - { + for ( int x = 0; x < length; x++ ) { GenericPropertyNode node = binTable.getProperty( x ); int pageNum = LittleEndian.getInt( node.getBytes() ); @@ -535,12 +472,10 @@ public final class HWPFLister System.out.println( "* PFKP: " + pfkp ); - for ( PAPX papx : pfkp.getPAPXs() ) - { + for ( PAPX papx : pfkp.getPAPXs() ) { System.out.println( "** " + papx ); papxs.add( papx ); - if ( papx != null && withSprms ) - { + if ( papx != null && withSprms ) { SprmIterator sprmIt = new SprmIterator( papx.getGrpprl(), 2 ); dumpSprms( sprmIt, "*** " ); @@ -551,11 +486,9 @@ public final class HWPFLister Collections.sort( papxs ); System.out.println( "* Sorted by END" ); - for ( PAPX papx : papxs ) - { + for ( PAPX papx : papxs ) { System.out.println( "** " + papx ); - if ( papx != null && withSprms ) - { + if ( papx != null && withSprms ) { SprmIterator sprmIt = new SprmIterator( papx.getGrpprl(), 2 ); dumpSprms( sprmIt, "*** " ); } @@ -563,12 +496,10 @@ public final class HWPFLister } - for ( PAPX papx : _doc.getParagraphTable().getParagraphs() ) - { + for ( PAPX papx : _doc.getParagraphTable().getParagraphs() ) { System.out.println( papx ); - if ( withProperties ) - { + if ( withProperties ) { Paragraph paragraph = Paragraph.newParagraph( _doc.getOverallRange(), papx ); System.out.println( paragraph.getProps() ); } @@ -578,23 +509,18 @@ public final class HWPFLister } } - public void dumpParagraphs( boolean dumpAssotiatedPapx ) - { - for ( Map.Entry<Integer, String> entry : paragraphs.entrySet() ) - { + public void dumpParagraphs( boolean dumpAssotiatedPapx ) { + for ( Map.Entry<Integer, String> entry : paragraphs.entrySet() ) { Integer endOfParagraphCharOffset = entry.getKey(); System.out.println( "[...; " + ( endOfParagraphCharOffset + 1 ) + "): " + entry.getValue() ); - if ( dumpAssotiatedPapx ) - { + if ( dumpAssotiatedPapx ) { boolean hasAssotiatedPapx = false; - for ( PAPX papx : _doc.getParagraphTable().getParagraphs() ) - { + for ( PAPX papx : _doc.getParagraphTable().getParagraphs() ) { if ( papx.getStart() <= endOfParagraphCharOffset.intValue() && endOfParagraphCharOffset.intValue() < papx - .getEnd() ) - { + .getEnd() ) { hasAssotiatedPapx = true; System.out.println( "* " + papx ); @@ -603,8 +529,7 @@ public final class HWPFLister dumpSprms( sprmIt, "** " ); } } - if ( !hasAssotiatedPapx ) - { + if ( !hasAssotiatedPapx ) { System.out.println( "* " + "NO PAPX ASSOTIATED WITH PARAGRAPH!" ); } @@ -612,20 +537,16 @@ public final class HWPFLister } } - protected void dumpSprms( SprmIterator sprmIt, String linePrefix ) - { - while ( sprmIt.hasNext() ) - { + protected void dumpSprms( SprmIterator sprmIt, String linePrefix ) { + while ( sprmIt.hasNext() ) { SprmOperation sprm = sprmIt.next(); System.out.println( linePrefix + sprm); } } - public void dumpParagraphsDom( boolean withText ) - { + public void dumpParagraphsDom( boolean withText ) { Range range = _doc.getOverallRange(); - for ( int p = 0; p < range.numParagraphs(); p++ ) - { + for ( int p = 0; p < range.numParagraphs(); p++ ) { Paragraph paragraph = range.getParagraph( p ); System.out.println( p + ":\t" + paragraph); @@ -634,33 +555,27 @@ public final class HWPFLister } } - private void dumpPictures() - { - if ( _doc instanceof HWPFOldDocument ) - { + private void dumpPictures() { + if ( _doc instanceof HWPFOldDocument ) { System.out.println( "Word 95 not supported so far" ); return; } List<Picture> allPictures = ( (HWPFDocument) _doc ).getPicturesTable() .getAllPictures(); - for ( Picture picture : allPictures ) - { + for ( Picture picture : allPictures ) { System.out.println(picture); } } - private void dumpStyles() - { - if ( _doc instanceof HWPFOldDocument ) - { + private void dumpStyles() { + if ( _doc instanceof HWPFOldDocument ) { System.out.println( "Word 95 not supported so far" ); return; } HWPFDocument hwpfDocument = (HWPFDocument) _doc; - for ( int s = 0; s < hwpfDocument.getStyleSheet().numStyles(); s++ ) - { + for ( int s = 0; s < hwpfDocument.getStyleSheet().numStyles(); s++ ) { StyleDescription styleDescription = hwpfDocument.getStyleSheet() .getStyleDescription( s ); if ( styleDescription == null ) @@ -681,32 +596,27 @@ public final class HWPFLister } protected void dumpParagraphLevels( ListTables listTables, - ParagraphProperties paragraph ) - { - if ( paragraph.getIlfo() != 0 ) - { + ParagraphProperties paragraph ) { + if ( paragraph.getIlfo() != 0 ) { final LFO lfo = listTables.getLfo( paragraph.getIlfo() ); System.out.println( "PAP's LFO: " + lfo ); final LFOData lfoData = listTables.getLfoData( paragraph.getIlfo() ); System.out.println( "PAP's LFOData: " + lfoData ); - if ( lfo != null ) - { + if ( lfo != null ) { final ListLevel listLevel = listTables.getLevel( lfo.getLsid(), paragraph.getIlvl() ); System.out.println( "PAP's ListLevel: " + listLevel ); - if ( listLevel.getGrpprlPapx() != null ) - { + if ( listLevel.getGrpprlPapx() != null ) { System.out.println( "PAP's ListLevel PAPX:" ); dumpSprms( new SprmIterator( listLevel.getGrpprlPapx(), 0 ), "* " ); } - if ( listLevel.getGrpprlPapx() != null ) - { + if ( listLevel.getGrpprlPapx() != null ) { System.out.println( "PAP's ListLevel CHPX:" ); dumpSprms( new SprmIterator( listLevel.getGrpprlChpx(), 0 ), @@ -716,14 +626,11 @@ public final class HWPFLister } } - public void dumpTextPieces( boolean withText ) - { - for ( TextPiece textPiece : _doc.getTextTable().getTextPieces() ) - { + public void dumpTextPieces( boolean withText ) { + for ( TextPiece textPiece : _doc.getTextTable().getTextPieces() ) { System.out.println( textPiece ); - if ( withText ) - { + if ( withText ) { System.out.println( "\t" + textPiece.getStringBuilder() ); } } diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/FibBase.java b/src/scratchpad/src/org/apache/poi/hwpf/model/FibBase.java index e7734a9539..5f4b6e82bf 100644 --- a/src/scratchpad/src/org/apache/poi/hwpf/model/FibBase.java +++ b/src/scratchpad/src/org/apache/poi/hwpf/model/FibBase.java @@ -31,22 +31,18 @@ import org.apache.poi.util.Internal; * v20110608 Word (.doc) Binary File Format */ @Internal -public class FibBase extends FibBaseAbstractType -{ +public class FibBase extends FibBaseAbstractType { - public FibBase() - { + public FibBase() { } - public FibBase( byte[] std, int offset ) - { + public FibBase( byte[] std, int offset ) { fillFields( std, offset ); } @Override @SuppressWarnings( "deprecation" ) - public boolean equals( Object obj ) - { + public boolean equals( Object obj ) { if ( this == obj ) return true; if ( obj == null ) @@ -87,8 +83,7 @@ public class FibBase extends FibBaseAbstractType @Override @SuppressWarnings( "deprecation" ) - public int hashCode() - { + public int hashCode() { final int prime = 31; int result = 1; result = prime * result + field_10_flags2; @@ -107,5 +102,4 @@ public class FibBase extends FibBaseAbstractType result = prime * result + field_9_envr; return result; } - } diff --git a/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java b/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java index b4885f1d23..c6853f156c 100644 --- a/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java +++ b/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java @@ -286,4 +286,35 @@ public class TestVBAMacroReader { assertContains(content, "Attribute VB_Customizable = True"); r.close(); } + + @Test + public void bug62624() throws IOException { + //macro comes from Common Crawl: HRLOXHGMGLFIJQQU27RIWXOARRHAAAAS + File f = POIDataSamples.getSpreadSheetInstance().getFile("62624.bin"); + VBAMacroReader r = new VBAMacroReader(f); + + Map<String, Module> macros = r.readMacroModules(); + assertEquals(13, macros.size()); + assertNotNull(macros.get("M\u00F3dulo1")); + assertContains(macros.get("M\u00F3dulo1").getContent(), "Calcula_tributos"); + assertEquals(Module.ModuleType.Module, macros.get("M\u00F3dulo1").geModuleType()); + r.close(); + } + + @Test + public void bug62625() throws IOException { + //macro comes from Common Crawl: 4BZ22N5QG5R2SUU2MNN47PO7VBQLNYIQ + //A REFERENCE_NAME can sometimes only have an ascii string without + //a reserved byte followed by the unicode string. + //See https://github.com/decalage2/oletools/blob/master/oletools/olevba.py#L1516 + //and https://github.com/decalage2/oletools/pull/135 from (@c1fe) + + + File f = POIDataSamples.getSpreadSheetInstance().getFile("62625.bin"); + VBAMacroReader r = new VBAMacroReader(f); + + Map<String, Module> macros = r.readMacroModules(); + assertEquals(20, macros.size()); + r.close(); + } } diff --git a/src/testcases/org/apache/poi/ss/formula/functions/AllSpreadsheetBasedTests.java b/src/testcases/org/apache/poi/ss/formula/functions/AllSpreadsheetBasedTests.java index 2b34dcf8a4..0dc5ed9233 100644 --- a/src/testcases/org/apache/poi/ss/formula/functions/AllSpreadsheetBasedTests.java +++ b/src/testcases/org/apache/poi/ss/formula/functions/AllSpreadsheetBasedTests.java @@ -41,6 +41,7 @@ import org.junit.runners.Suite; TestQuotientFunctionsFromSpreadsheet.class, TestReptFunctionsFromSpreadsheet.class, TestRomanFunctionsFromSpreadsheet.class, + TestTrendFunctionsFromSpreadsheet.class, TestWeekNumFunctionsFromSpreadsheet.class, TestWeekNumFunctionsFromSpreadsheet2013.class }) diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestFrequency.java b/src/testcases/org/apache/poi/ss/formula/functions/TestFrequency.java new file mode 100644 index 0000000000..c441c82ade --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestFrequency.java @@ -0,0 +1,91 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.ss.formula.functions; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.junit.Test; + +import static org.apache.poi.ss.formula.functions.Frequency.histogram; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * Testcase for the function FREQUENCY(data, bins) + * + * @author Yegor Kozlov + */ +public class TestFrequency { + + @Test + public void testHistogram() { + assertArrayEquals(new int[]{3, 2, 2, 0, 1, 1}, + histogram( + new double[]{11, 12, 13, 21, 29, 36, 40, 58, 69}, + new double[]{20, 30, 40, 50, 60}) + ); + + assertArrayEquals(new int[]{1, 1, 1, 1, 1, 0}, + histogram( + new double[]{20, 30, 40, 50, 60}, + new double[]{20, 30, 40, 50, 60}) + + ); + + assertArrayEquals(new int[]{2, 3}, + histogram( + new double[]{20, 30, 40, 50, 60}, + new double[]{30}) + + ); + } + + @Test + public void testEvaluate() { + Workbook wb = new HSSFWorkbook(); + FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); + + int[] data = {1, 1, 2, 3, 4, 4, 5, 7, 8, 9, 9, 11, 3, 5, 8}; + int[] bins = {3, 6, 9}; + Sheet sheet = wb.createSheet(); + Row dataRow = sheet.createRow(0); // A1:O1 + for (int i = 0; i < data.length; i++) { + dataRow.createCell(i).setCellValue(data[i]); + } + Row binsRow = sheet.createRow(1); + for (int i = 0; i < bins.length; i++) { // A2:C2 + binsRow.createCell(i).setCellValue(bins[i]); + } + Row fmlaRow = sheet.createRow(2); + CellRange<? extends Cell> arrayFmla = sheet.setArrayFormula("FREQUENCY(A1:O1,A2:C2)", CellRangeAddress.valueOf("A3:A6")); + Cell b3 = fmlaRow.createCell(1); // B3 + b3.setCellFormula("COUNT(FREQUENCY(A1:O1,A2:C2))"); // frequency returns a vertical array of bins+1 + + Cell c3 = fmlaRow.createCell(2); + c3.setCellFormula("SUM(FREQUENCY(A1:O1,A2:C2))"); // sum of the frequency bins should add up to the number of data values + + assertEquals(5, (int) evaluator.evaluate(arrayFmla.getFlattenedCells()[0]).getNumberValue()); + assertEquals(4, (int) evaluator.evaluate(arrayFmla.getFlattenedCells()[1]).getNumberValue()); + assertEquals(5, (int) evaluator.evaluate(arrayFmla.getFlattenedCells()[2]).getNumberValue()); + assertEquals(1, (int) evaluator.evaluate(arrayFmla.getFlattenedCells()[3]).getNumberValue()); + + assertEquals(4, (int) evaluator.evaluate(b3).getNumberValue()); + assertEquals(15, (int) evaluator.evaluate(c3).getNumberValue()); + + } +} diff --git a/src/testcases/org/apache/poi/ss/formula/functions/TestTrendFunctionsFromSpreadsheet.java b/src/testcases/org/apache/poi/ss/formula/functions/TestTrendFunctionsFromSpreadsheet.java new file mode 100644 index 0000000000..51871d16e5 --- /dev/null +++ b/src/testcases/org/apache/poi/ss/formula/functions/TestTrendFunctionsFromSpreadsheet.java @@ -0,0 +1,31 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.ss.formula.functions; + +import java.util.Collection; + +import org.junit.runners.Parameterized.Parameters; + +/** +* Tests TREND() as loaded from a test data spreadsheet. +*/ +public class TestTrendFunctionsFromSpreadsheet extends BaseTestFunctionsFromSpreadsheet { + @Parameters(name="{0}") + public static Collection<Object[]> data() throws Exception { + return data(TestTrendFunctionsFromSpreadsheet.class, "Trend.xls"); + } +} diff --git a/src/testcases/org/apache/poi/ss/util/TestDateFormatConverter.java b/src/testcases/org/apache/poi/ss/util/TestDateFormatConverter.java index 0d2710425f..811a59701b 100644 --- a/src/testcases/org/apache/poi/ss/util/TestDateFormatConverter.java +++ b/src/testcases/org/apache/poi/ss/util/TestDateFormatConverter.java @@ -99,15 +99,6 @@ public final class TestDateFormatConverter { row.createCell(5).setCellValue(javaDateFormatPattern); row.createCell(6).setCellValue(excelFormatPattern); } catch (Exception e) { - // this can be removed after https://bugs.openjdk.java.net/browse/JDK-8209047 is available - // in JDK 11 ea > 26 - if(locale.toString().startsWith("my") && - e.getMessage().contains("Illegal pattern character 'B'") && - System.getProperty("java.version").startsWith("11")) { - System.out.println("DateFormat.getDateTimeInstance() fails for Malaysian Locale on JDK 11, submitted bug report to Oracle"); - continue; - } - throw new RuntimeException( "Failed for locale: " + locale + " and style " + style + "\n" + "Having locales: " + Arrays.toString(DateFormat.getAvailableLocales()), e); @@ -153,7 +144,6 @@ public final class TestDateFormatConverter { DateFormatConverter.getPrefixForLocale(new Locale("")); } - @Ignore("Fails on JDK 11, submitted as ID : 9056763") @Test public void testJDK11MyLocale() { DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.forLanguageTag("my")); |