From 65b7c9e273083921f0596524932cbf9c4222454a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alain=20B=C3=A9arez?= Date: Wed, 10 Oct 2018 17:49:53 +0000 Subject: [PATCH 01/33] fix most alerts reported by LGTM on OOXML git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1843481 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/poifs/crypt/dsig/SignatureConfig.java | 100 +++++----- .../apache/poi/xdgf/usermodel/XDGFShape.java | 181 ++++++++++++------ .../apache/poi/xdgf/usermodel/XDGFText.java | 22 ++- .../poi/xdgf/util/HierarchyPrinter.java | 24 +-- .../org/apache/poi/xdgf/util/VsdxToPng.java | 19 +- .../poi/xslf/usermodel/XMLSlideShow.java | 6 +- .../apache/poi/xslf/usermodel/XSLFColor.java | 32 ++-- .../apache/poi/xslf/usermodel/XSLFNotes.java | 6 +- .../apache/poi/xslf/usermodel/XSLFShape.java | 93 +++++---- .../org/apache/poi/xslf/util/PPTX2PNG.java | 16 +- .../apache/poi/xssf/binary/XSSFBUtils.java | 2 +- .../apache/poi/xssf/streaming/SXSSFRow.java | 44 +++-- .../poi/xssf/usermodel/XSSFDrawing.java | 2 +- .../apache/poi/xssf/usermodel/XSSFRow.java | 77 +++++--- .../poi/xwpf/usermodel/XWPFSDTContent.java | 20 +- .../apache/poi/xwpf/usermodel/XWPFTable.java | 107 ++++++----- 16 files changed, 429 insertions(+), 322 deletions(-) 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 = new ThreadLocal<>(); private ThreadLocal signatureFactory = new ThreadLocal<>(); private ThreadLocal keyInfoFactory = new ThreadLocal<>(); private ThreadLocal provider = new ThreadLocal<>(); - + private List 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 null the signature will be limited to XAdES-T only. @@ -148,16 +148,16 @@ public class SignatureConfig { * null 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 null, 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 null, 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 { *
  • the Santuario xmlsec provider
  • *
  • the JDK xmlsec provider
  • * - * + * * 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 + ": "; - else + } else { return ""; + } } 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 section : _sections.entrySet()) { XDGFSection master = _masterShape.getSection(section.getKey()); - if (master != null) + if (master != null) { section.getValue().setupMaster(master); + } } for (Entry 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 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 7706ca19e3..815eaee826 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 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 { * * @param part the package part holding the notes data, * the content type must be application/vnd.openxmlformats-officedocument.notes+xml - * @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 { @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 { 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 { _shape = shape; _sheet = sheet; } - + /** * @return the xml bean holding this shape's data */ @@ -91,11 +91,12 @@ public abstract class XSLFShape implements Shape { // 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 { 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 fetcher = new PropertyFetcher() { + @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 { 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 { setValue(paint); return true; } - - + + return false; } }; @@ -181,16 +184,16 @@ public abstract class XSLFShape implements Shape { 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 { public Placeholder getPlaceholder() { return getPlaceholderDetails().getPlaceholder(); } - + /** * @see PlaceholderDetails#setPlaceholder(Placeholder) */ @@ -268,7 +271,9 @@ public abstract class XSLFShape implements Shape { @SuppressWarnings({"unchecked", "WeakerAccess"}) protected T selectProperty(Class 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 { *
  • slideLayout *
  • slideMaster * - * + * * 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 { return false; } MasterSheet sm = getSheet().getMasterSheet(); - + // try slide layout if (sm instanceof XSLFSlideLayout) { XSLFSlideLayout slideLayout = (XSLFSlideLayout)sm; @@ -309,7 +314,7 @@ public abstract class XSLFShape implements Shape { } sm = slideLayout.getMasterSheet(); } - + // try slide master if (sm instanceof XSLFSlideMaster) { XSLFSlideMaster master = (XSLFSlideMaster)sm; @@ -317,15 +322,15 @@ public abstract class XSLFShape implements Shape { 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 { throw new RuntimeException(e); } } - + + @Override public InputStream getImageData() { try { return getPart().getInputStream(); @@ -406,17 +412,19 @@ public abstract class XSLFShape implements Shape { } } + @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 { 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 { 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 { 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 { 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 { 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 79327bce4f..b3288d46f3 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); } @@ -169,7 +169,7 @@ public class PPTX2PNG { System.out.println("Done"); } } - + private static Set slideIndexes(final int slideCount, String range) { Set slideIdx = new TreeSet<>(); if ("-1".equals(range)) { 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 { private static final Boolean UNDEFINED = null; - + private final SXSSFSheet _sheet; // parent sheet private final SortedMap _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 allCellsIterator() { return new CellIterator(); @@ -71,7 +71,7 @@ public class SXSSFRow implements Row, Comparable 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 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 @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 { 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 */ @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 null. @@ -440,7 +443,7 @@ public class SXSSFRow implements Row, Comparable /** * 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 @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 throw new UnsupportedOperationException(); } } - + /** * Compares two SXSSFRow 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 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/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 { _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 { 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 { */ @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 { @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 { */ @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 { 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 { } } else { StylesTable styleSource = getSheet().getWorkbook().getStylesSource(); - + XSSFCellStyle xStyle = (XSSFCellStyle)style; xStyle.verifyBelongsToStylesSource(styleSource); @@ -458,7 +466,7 @@ public class XSSFRow implements Row, Comparable { _row.setCustomFormat(true); } } - + /** * Remove the Cell from this row. * @@ -502,8 +510,8 @@ public class XSSFRow implements Row, Comparable { 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 { } 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 { 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 { 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 { */ @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 { */ @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 { 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/xwpf/usermodel/XWPFSDTContent.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java index 587674edc3..99ab66a7ee 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java @@ -40,16 +40,16 @@ public class XWPFSDTContent implements ISDTContent { // private final IBody part; // private final XWPFDocument document; - private List paragraphs = new ArrayList<>(); - private List tables = new ArrayList<>(); - private List runs = new ArrayList<>(); - private List contentControls = new ArrayList<>(); + // private List paragraphs = new ArrayList<>(); + // private List tables = new ArrayList<>(); + // private List runs = new ArrayList<>(); + // private List contentControls = new ArrayList<>(); private List bodyElements = new ArrayList<>(); public XWPFSDTContent(CTSdtContentRun sdtRun, IBody part, IRunBody parent) { for (CTR ctr : sdtRun.getRArray()) { XWPFRun run = new XWPFRun(ctr, parent); - runs.add(run); + // runs.add(run); bodyElements.add(run); } } @@ -62,24 +62,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 +131,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. *

    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%).

    + * the width type is PCT, the value is the percentage times 50 (e.g., 2500 for 50%).

    * @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. *

    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()); } /** From 870c1b2cf567209a0c159415cefd89a1615c3e2c Mon Sep 17 00:00:00 2001 From: Tim Allison Date: Thu, 11 Oct 2018 14:14:07 +0000 Subject: [PATCH 02/33] 62815 -- some numeric values not correctly extracted from xlsb git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1843553 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/xssf/binary/XSSFBSheetHandler.java | 6 +- .../TestXSSFBEventBasedExcelExtractor.java | 23 ++ test-data/spreadsheet/62815.xlsb | Bin 0 -> 10665 bytes test-data/spreadsheet/62815.xlsb.txt | 284 ++++++++++++++++++ 4 files changed, 310 insertions(+), 3 deletions(-) create mode 100644 test-data/spreadsheet/62815.xlsb create mode 100644 test-data/spreadsheet/62815.xlsb.txt 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/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/test-data/spreadsheet/62815.xlsb b/test-data/spreadsheet/62815.xlsb new file mode 100644 index 0000000000000000000000000000000000000000..66d8e652b0832340befba4bbcffefafbad1921f3 GIT binary patch literal 10665 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 Date: Thu, 11 Oct 2018 14:50:27 +0000 Subject: [PATCH 03/33] fix more LGTM alerts, in tests git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1843557 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/ooxml/util/OOXMLLite.java | 73 +++--- .../poi/openxml4j/opc/ZipFileAssert.java | 202 +++++++++-------- .../xslf/usermodel/TestXSLFTextParagraph.java | 24 +- .../apache/poi/xssf/XSSFTestDataSamples.java | 57 ++--- .../poi/xssf/usermodel/TestXSSFBugs.java | 52 +++-- .../xssf/usermodel/TestXSSFColGrouping.java | 88 ++++---- .../usermodel/TestXSSFDataValidation.java | 59 ++--- .../poi/xssf/usermodel/TestXSSFSheet.java | 213 +++++++++--------- .../poi/xssf/usermodel/TestXSSFWorkbook.java | 6 +- .../usermodel/charts/TestXSSFChartTitle.java | 2 +- .../org/apache/poi/xssf/util/MemoryUsage.java | 42 ++-- 11 files changed, 438 insertions(+), 380 deletions(-) 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,7 +17,25 @@ package org.apache.poi.ooxml.util; -import junit.framework.TestCase; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +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; @@ -29,19 +47,7 @@ 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; -import java.lang.reflect.Method; -import java.net.URL; -import java.security.AccessController; -import java.security.CodeSource; -import java.security.PrivilegedAction; -import java.security.ProtectionDomain; -import java.util.*; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.regex.Pattern; +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> 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() { + @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 ZipFileAssert() { + } - private static void equals( - TreeMap file1, - TreeMap file2) { - Set 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); + private static void equals( + TreeMap file1, + TreeMap file2) { + Set listFile1 = file1.keySet(); + Assert.assertEquals("not the same number of files in zip:", listFile1.size(), file2.keySet().size()); - assertNotNull(fileName + " not found in 2nd zip", contain2); - // no need to check for contain1. The key come from it + for (String fileName : listFile1) { + // extract the contents for both + ByteArrayOutputStream contain1 = file1.get(fileName); + ByteArrayOutputStream contain2 = file2.get(fileName); - 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()); + 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()); - } - } - } + assertArrayEquals("contents differ", contain1.toByteArray(), contain2.toByteArray()); + } + } + } - private static TreeMap decompress( - File filename) throws IOException { - // store the zip content in memory - // let s assume it is not Go ;-) - TreeMap zipContent = new TreeMap<>(); + private static TreeMap decompress( + File filename) throws IOException { + // store the zip content in memory + // let s assume it is not Go ;-) + TreeMap zipContent = new TreeMap<>(); - /* Open file to decompress */ - FileInputStream file_decompress = new FileInputStream(filename); + try ( + /* Open file to decompress */ + FileInputStream file_decompress = new FileInputStream(filename); - /* Create a buffer for the decompressed files */ - BufferedInputStream buffi = new BufferedInputStream(file_decompress); + /* Create a buffer for the decompressed files */ + BufferedInputStream buffi = new BufferedInputStream(file_decompress); - /* Open the file with the buffer */ - ZipArchiveInputStream zis = new ZipArchiveInputStream(buffi); + /* Open the file with the buffer */ + ZipArchiveInputStream zis = new ZipArchiveInputStream(buffi); + ) { - /* Processing entries of the zip file */ - ArchiveEntry entree; - while ((entree = zis.getNextEntry()) != null) { + /* 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); - } + /* Create a array for the current entry */ + ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); + IOUtils.copy(zis, byteArray); + zipContent.put(entree.getName(), byteArray); + } - zis.close(); + } - return zipContent; - } + return zipContent; + } - /** - * Asserts that two files are equal. Throws an AssertionFailedError - * if they are not. - *

    - * - */ - public static void assertEquals(File expected, File actual) { - assertNotNull(expected); - assertNotNull(actual); + /** + * Asserts that two files are equal. Throws an AssertionFailedError + * if they are not. + *

    + * + */ + 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("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()); + assertTrue("Expected file not readable", expected.canRead()); + assertTrue("Actual file not readable", actual.canRead()); - try { - TreeMap file1 = decompress(expected); - TreeMap file2 = decompress(actual); - equals(file1, file2); - } catch (IOException e) { - throw new AssertionFailedError(e.toString()); - } - } + try { + TreeMap file1 = decompress(expected); + TreeMap 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; - } - } + 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; - } - } + return outcome; + } + } } 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 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 wb to {@link #TEST_OUTPUT_DIR}/testName.xlsx * (or create a temporary file if TEST_OUTPUT_DIR is not defined). @@ -78,11 +77,13 @@ public class XSSFTestDataSamples { writeOut(wb, file); return file; } - + private static 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 wb 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 wb, * 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 wb 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"; - * + * * * 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 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/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java index 70a5092a07..a0cd5bec7f 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; @@ -3290,7 +3314,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 +3324,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 +3333,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 * 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 * - * + * */ 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 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 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 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 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 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 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/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> 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> 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> 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/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 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.util.CellReference; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; - -import java.util.List; import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +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.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 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. *

    * 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()); From e95fc069293db1fd4988053c6cc078af1287612c Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Wed, 17 Oct 2018 14:20:02 +0000 Subject: [PATCH 04/33] Failing unit test for bug #62831 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844115 13f79535-47bb-0310-9956-ffa450edef68 --- .classpath | 2 +- .../apache/poi/ss/TestWorkbookFactory.java | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.classpath b/.classpath index fc3394904a..89d75f475b 100644 --- a/.classpath +++ b/.classpath @@ -36,7 +36,7 @@ - + diff --git a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java index ade80688d0..e3c631c1da 100644 --- a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java +++ b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java @@ -39,6 +39,7 @@ 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.Ignore; import org.junit.Test; public final class TestWorkbookFactory { @@ -387,4 +388,34 @@ public final class TestWorkbookFactory { } } + /** + * See Bugzilla bug #62831 - #WorkbookFactory.create(File) needs + * to work for sub-classes of File too, eg JFileChooser + */ + @Test + @Ignore + public void testFileSubclass() throws Exception { + Workbook wb; + + 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()); + + wb = WorkbookFactory.create(altXLS); + assertNotNull(wb); + assertTrue(wb instanceof HSSFWorkbook); + + wb = WorkbookFactory.create(altXLSX); + assertNotNull(wb); + assertTrue(wb instanceof XSSFWorkbook); + } + + private static class TestFile extends File { + public TestFile(String file) { + super(file); + } + } } From f490cd7c5de69dd057ae2a7e5067baaf503bb792 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Wed, 17 Oct 2018 14:24:59 +0000 Subject: [PATCH 05/33] #62831 Fix WorkbookFactory.create with a subclass of File, eg from JFileChooser git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844116 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/ss/usermodel/WorkbookFactory.java | 7 +++++++ .../org/apache/poi/ss/TestWorkbookFactory.java | 16 +++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java b/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java index 50833aa6c2..aae8c043ce 100644 --- a/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java +++ b/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java @@ -296,6 +296,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 +312,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/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java index e3c631c1da..32978df88c 100644 --- a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java +++ b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java @@ -17,18 +17,24 @@ 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.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,10 +42,7 @@ 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.Ignore; import org.junit.Test; public final class TestWorkbookFactory { @@ -393,7 +396,6 @@ public final class TestWorkbookFactory { * to work for sub-classes of File too, eg JFileChooser */ @Test - @Ignore public void testFileSubclass() throws Exception { Workbook wb; From 8bf556e856f4e77dee51af448e159b0df44fbc5a Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Thu, 18 Oct 2018 13:46:04 +0000 Subject: [PATCH 06/33] Bug 62373: Support for FREQUENCY function git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844238 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/ss/formula/eval/FunctionEval.java | 3 +- .../poi/ss/formula/functions/Frequency.java | 81 +++++++++++++++++ .../ss/formula/functions/TestFrequency.java | 91 +++++++++++++++++++ 3 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 src/java/org/apache/poi/ss/formula/functions/Frequency.java create mode 100644 src/testcases/org/apache/poi/ss/formula/functions/TestFrequency.java 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..8442f5832f 100644 --- a/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java +++ b/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java @@ -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()
    + * Returns a frequency distribution as a vertical array

    + *

    + * Syntax
    + * FREQUENCY(data_array, bins_array)

    + *

    + * data_array 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.
    + * bins_array 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.
    + * + * @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/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 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()); + + } +} From 6b433ae8b34b8c32a0457d74fe3b023222fe7945 Mon Sep 17 00:00:00 2001 From: Greg Woolsey Date: Fri, 19 Oct 2018 01:11:47 +0000 Subject: [PATCH 07/33] #62834 FormulaEvaluator.evaluateInCell() throws Exception added cell type = formula check when looping through the shared formula range, to ignore any non-formula cells. Also refactored a bit to enable passing in the evaluation context, as getCellFormula() uses it behind the scenes when evaluating a shared formula cell (has to shift the formula references based on the master cell). Review of these changes is welcome, as always. Checked all other code referencing the "SHARED" enum, and didn't see anything else that dealt with formula cell values and thus would need to notice non-formula cells. Added unit test based on the failing file from Bugzilla. Test failed until the fixed code was in place. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844295 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/ss/formula/BaseFormulaEvaluator.java | 26 ++++++++++- .../org/apache/poi/ss/usermodel/Sheet.java | 5 +- .../usermodel/BaseXSSFFormulaEvaluator.java | 9 ++++ .../apache/poi/xssf/usermodel/XSSFCell.java | 23 ++++++++-- .../apache/poi/xssf/usermodel/XSSFSheet.java | 8 ++-- .../usermodel/TestXSSFFormulaEvaluation.java | 43 +++++++++++++++++- test-data/spreadsheet/62834.xlsx | Bin 0 -> 9629 bytes 7 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 test-data/spreadsheet/62834.xlsx 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 @@ -64,6 +64,14 @@ public abstract class BaseFormulaEvaluator implements FormulaEvaluator, Workbook return _bookEvaluator; } + /** + * 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 @@ -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/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 { /** * Sets array formula to specified region for result. - * + *

    + * Note if there are shared formulas this will invalidate any + * {@link FormulaEvaluator} instances based on this workbook + *

    * @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/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java index adb951d77a..0e41e50cb5 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,11 @@ public abstract class BaseXSSFFormulaEvaluator extends BaseFormulaEvaluator { } throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")"); } + + protected void setCellType(Cell cell, CellType cellType) { + EvaluationWorkbook evaluationWorkbook = getEvaluationWorkbook(); + BaseXSSFEvaluationWorkbook xewb = BaseXSSFEvaluationWorkbook.class.isAssignableFrom(evaluationWorkbook.getClass()) ? (BaseXSSFEvaluationWorkbook) evaluationWorkbook : null; + + ((XSSFCell) cell).setCellType(cellType, xewb); + } } 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)} *

    + *

    + * Note, if there are any shared formulas, his will invalidate any + * {@link FormulaEvaluator} instances based on this workbook. + *

    * * @param formula the formula to set, e.g. "SUM(C4:E4)". * If the argument is null 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/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 423561b2ce..cd9620d79c 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -4621,8 +4621,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 + * @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 +4636,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/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/test-data/spreadsheet/62834.xlsx b/test-data/spreadsheet/62834.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2f265e69c2be2d756a6eba0c11219c0f1114bea4 GIT binary patch literal 9629 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 literal 0 HcmV?d00001 From 59ee53ca71cc2e97c77f43bb92e6e0c6ed1af4e6 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Oct 2018 07:43:04 +0000 Subject: [PATCH 08/33] fix class cast issur recently introduced in BaseXSSFFormulaEvaluator git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844311 13f79535-47bb-0310-9956-ffa450edef68 --- .../xssf/usermodel/BaseXSSFFormulaEvaluator.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) 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 0e41e50cb5..bbad20e518 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/BaseXSSFFormulaEvaluator.java @@ -73,9 +73,14 @@ public abstract class BaseXSSFFormulaEvaluator extends BaseFormulaEvaluator { } protected void setCellType(Cell cell, CellType cellType) { - EvaluationWorkbook evaluationWorkbook = getEvaluationWorkbook(); - BaseXSSFEvaluationWorkbook xewb = BaseXSSFEvaluationWorkbook.class.isAssignableFrom(evaluationWorkbook.getClass()) ? (BaseXSSFEvaluationWorkbook) evaluationWorkbook : null; - - ((XSSFCell) cell).setCellType(cellType, xewb); + 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); + } } } From 7606b9293a4152fef3b3408d3f3212ab4abd35d0 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 26 Oct 2018 09:32:01 +0000 Subject: [PATCH 09/33] Remove the workaround for a bug in JDK 11 which was fixed in ea+28 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844874 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/ss/util/TestDateFormatConverter.java | 10 ---------- 1 file changed, 10 deletions(-) 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")); From e55b94a9e12a39bec41ec6556a36ac66132e2554 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 26 Oct 2018 09:32:13 +0000 Subject: [PATCH 10/33] Jenkins DSL: Adjust the view-description, but it is still commented out git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844875 13f79535-47bb-0310-9956-ffa450edef68 --- jenkins/create_jobs.groovy | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/jenkins/create_jobs.groovy b/jenkins/create_jobs.groovy index 6095a57e06..6e683bfb83 100644 --- a/jenkins/create_jobs.groovy +++ b/jenkins/create_jobs.groovy @@ -610,7 +610,24 @@ dashboardView("P/POI-new") { //lastSuccessDescription() jacoco() } - description("Jobs related to building/testing Apache POI") + description("\n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
    \n" + + "

    Apache POI - the Java API for Microsoft Documents

    \n" + + "

    Most of the POI Jobs are automatically generated by Jenkins Job DSL\n" + + " at https://svn.apache.org/repos/asf/poi/trunk/jenkins,
    \n" + + " see https://github.com/jenkinsci/job-dsl-plugin/wiki\n" + + " for more details about the DSL.
    \n" + + "

    \n" + + "

    \n" + + " Findbugs report of latest build -\n" + + " Sonar reports -\n" + + " Coverage of latest build\n" + + "

    \n" + + "
    ") filterBuildQueue(false) filterExecutors(false) From 54cb2814135841b66aaa27b312701ff4aed0bca1 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 26 Oct 2018 09:32:19 +0000 Subject: [PATCH 11/33] Set NOSONAR and reformat code git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844876 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/hwpf/dev/HWPFLister.java | 287 ++++++------------ .../org/apache/poi/hwpf/model/FibBase.java | 16 +- 2 files changed, 102 insertions(+), 201 deletions(-) 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 \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 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 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 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 entry : paragraphs.entrySet() ) - { + public void dumpParagraphs( boolean dumpAssotiatedPapx ) { + for ( Map.Entry 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 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; } - } From 15518968ee5180fabb18a5d085ade86989a595a4 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 26 Oct 2018 09:32:24 +0000 Subject: [PATCH 12/33] Ignore directory ooxml-testlib git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844877 13f79535-47bb-0310-9956-ffa450edef68 --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 103731746e..ffb9ae397f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ log*.* forrest.properties compile-lib/ ooxml-lib/ +ooxml-testlib/ # Eclipse /bin From ebbceee6e79fdce9b3b93a91ce2243bfc21142cc Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 26 Oct 2018 09:32:30 +0000 Subject: [PATCH 13/33] Set NOSONAR, adjust test slightly and reformat code git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844878 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/xssf/streaming/TestSXSSFWorkbook.java | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) 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"); From 1d464ed131575ae9b1616877cb59a580b8acecbd Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 26 Oct 2018 09:32:43 +0000 Subject: [PATCH 14/33] Typos and IDE warnings git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844879 13f79535-47bb-0310-9956-ffa450edef68 --- .../examples/AddDimensionedImage.java | 16 +++++++-------- .../poi/ss/examples/AddDimensionedImage.java | 14 ++++++------- .../usermodel/examples/NewLinesInCells.java | 5 ++--- .../poi/ss/format/CellDateFormatter.java | 1 + .../poi/ss/format/CellTextFormatter.java | 20 +++++++------------ .../apache/poi/ss/formula/ptg/RefPtgBase.java | 2 +- src/java/org/apache/poi/ss/util/CellUtil.java | 16 +++++++-------- .../poi/xssf/usermodel/TestXSSFBugs.java | 10 +++++++++- 8 files changed, 43 insertions(+), 41 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/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/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/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/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/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java index a0cd5bec7f..ef83956468 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFBugs.java @@ -2993,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 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()); From a8fd70ec7fba1b66474f1fdde159b4764ad83c41 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 26 Oct 2018 09:32:57 +0000 Subject: [PATCH 15/33] Adjust sample for creating comments to also create a .xlsx file Enhance workbook factory to allow to create new empty workbooks as well git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844881 13f79535-47bb-0310-9956-ffa450edef68 --- .../hssf/usermodel/examples/CellComments.java | 74 ++++++++++++------- .../hssf/usermodel/HSSFWorkbookFactory.java | 9 +++ .../poi/ss/usermodel/WorkbookFactory.java | 18 +++++ .../exceptions/InvalidFormatException.java | 2 +- .../apache/poi/openxml4j/opc/OPCPackage.java | 2 + .../xssf/usermodel/XSSFWorkbookFactory.java | 15 ++-- .../apache/poi/ss/TestWorkbookFactory.java | 71 ++++++++++-------- 7 files changed, 129 insertions(+), 62 deletions(-) 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,19 +17,23 @@ package org.apache.poi.hssf.usermodel.examples; +import org.apache.poi.hssf.usermodel.HSSFComment; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +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; -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; - /** * Demonstrates how to work with excel cell comments.

    * @@ -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/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 @@ -31,6 +31,15 @@ import org.apache.poi.util.Internal; @SuppressWarnings("unused") @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

    * Note that in order to properly release resources the diff --git a/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java b/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java index aae8c043ce..832795a07f 100644 --- a/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java +++ b/src/java/org/apache/poi/ss/usermodel/WorkbookFactory.java @@ -43,6 +43,24 @@ import org.apache.poi.util.Removal; * by auto-detecting from the supplied input. */ 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

    * 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/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/testcases/org/apache/poi/ss/TestWorkbookFactory.java b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java index 32978df88c..878b536ec2 100644 --- a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java +++ b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java @@ -48,8 +48,8 @@ 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); @@ -199,7 +199,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 @@ -234,26 +233,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 @@ -261,9 +260,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 @@ -309,28 +308,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 @@ -338,9 +337,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 @@ -397,22 +396,22 @@ public final class TestWorkbookFactory { */ @Test public void testFileSubclass() throws Exception { - Workbook wb; - 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()); - - wb = WorkbookFactory.create(altXLS); - assertNotNull(wb); - assertTrue(wb instanceof HSSFWorkbook); - - wb = WorkbookFactory.create(altXLSX); - assertNotNull(wb); - assertTrue(wb instanceof XSSFWorkbook); + + try (Workbook wb = WorkbookFactory.create(altXLS)) { + assertNotNull(wb); + assertTrue(wb instanceof HSSFWorkbook); + } + + try (Workbook wb = WorkbookFactory.create(altXLSX)) { + assertNotNull(wb); + assertTrue(wb instanceof XSSFWorkbook); + } } private static class TestFile extends File { @@ -420,4 +419,18 @@ public final class TestWorkbookFactory { super(file); } } + + /** + * Check that the overloaded file methods which take passwords work properly + */ + @Test + public void testCreateEmpty() throws Exception { + try (Workbook wb = WorkbookFactory.create(false)) { + assertTrue(wb instanceof HSSFWorkbook); + } + + try (Workbook wb = WorkbookFactory.create(true)) { + assertTrue(wb instanceof XSSFWorkbook); + } + } } From 960cbb77146c550661c7f75ef86a151c0f4528d5 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 26 Oct 2018 10:58:37 +0000 Subject: [PATCH 16/33] IDE warnings, slightly more tests and fix test to not leave a modified file behind git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844894 13f79535-47bb-0310-9956-ffa450edef68 --- .../TestOPCComplianceCoreProperties.java | 8 +--- .../apache/poi/ss/TestWorkbookFactory.java | 46 +++++++++++++------ 2 files changed, 35 insertions(+), 19 deletions(-) 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/ss/TestWorkbookFactory.java b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java index 878b536ec2..c4c68b9717 100644 --- a/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java +++ b/src/ooxml/testcases/org/apache/poi/ss/TestWorkbookFactory.java @@ -31,6 +31,7 @@ import java.io.InputStream; import org.apache.poi.EmptyFileException; import org.apache.poi.EncryptedDocumentException; +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; @@ -109,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( @@ -403,15 +411,15 @@ public final class TestWorkbookFactory { assertTrue(altXLS.exists()); assertTrue(altXLSX.exists()); - try (Workbook wb = WorkbookFactory.create(altXLS)) { - assertNotNull(wb); - assertTrue(wb instanceof HSSFWorkbook); - } + Workbook wb = WorkbookFactory.create(altXLS); + assertNotNull(wb); + assertTrue(wb instanceof HSSFWorkbook); + closeOrRevert(wb); - try (Workbook wb = WorkbookFactory.create(altXLSX)) { - assertNotNull(wb); - assertTrue(wb instanceof XSSFWorkbook); - } + wb = WorkbookFactory.create(altXLSX); + assertNotNull(wb); + assertTrue(wb instanceof XSSFWorkbook); + closeOrRevert(wb); } private static class TestFile extends File { @@ -425,12 +433,24 @@ public final class TestWorkbookFactory { */ @Test public void testCreateEmpty() throws Exception { - try (Workbook wb = WorkbookFactory.create(false)) { - assertTrue(wb instanceof HSSFWorkbook); - } + Workbook wb = WorkbookFactory.create(false); + assertTrue(wb instanceof HSSFWorkbook); + closeOrRevert(wb); - try (Workbook wb = WorkbookFactory.create(true)) { - assertTrue(wb instanceof XSSFWorkbook); + 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 } } + } From af9142e4b744638fa448f81081a7cda1bc376576 Mon Sep 17 00:00:00 2001 From: Tim Allison Date: Fri, 26 Oct 2018 19:06:18 +0000 Subject: [PATCH 17/33] bug 62859 -- fix two potential NPEs when initializing XWPFSDTContent git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844920 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/xwpf/usermodel/XWPFSDTContent.java | 6 ++++++ .../testcases/org/apache/poi/sl/TestFonts.java | 2 +- .../apache/poi/xwpf/usermodel/TestXWPFSDT.java | 13 +++++++++++++ test-data/document/Bug62859.docx | Bin 0 -> 17326 bytes 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 test-data/document/Bug62859.docx 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 99ab66a7ee..f39c5cf482 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFSDTContent.java @@ -47,6 +47,9 @@ public class XWPFSDTContent implements ISDTContent { private List 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); @@ -55,6 +58,9 @@ public class XWPFSDTContent implements ISDTContent { } public XWPFSDTContent(CTSdtContentBlock block, IBody part, IRunBody parent) { + if (block == null) { + return; + } XmlCursor cursor = block.newCursor(); cursor.selectPath("./*"); while (cursor.toNextSelection()) { diff --git a/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java b/src/ooxml/testcases/org/apache/poi/sl/TestFonts.java index b7a6d16a54..f35065de75 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/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 sdts = extractAllSDTs(doc); + assertEquals(1, sdts.size()); + assertEquals("", sdts.get(0).getTag()); + assertEquals("", sdts.get(0).getTitle()); + } + private List extractAllSDTs(XWPFDocument doc) { List sdts = new ArrayList<>(); diff --git a/test-data/document/Bug62859.docx b/test-data/document/Bug62859.docx new file mode 100644 index 0000000000000000000000000000000000000000..e0ede4a83db6a202989695236065e72c8884bf79 GIT binary patch literal 17326 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 literal 0 HcmV?d00001 From cc131eeab945f374729322f7e09ce31bce4dab05 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 27 Oct 2018 11:52:26 +0000 Subject: [PATCH 18/33] update xmlbeans to 3.0.2 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1844955 13f79535-47bb-0310-9956-ffa450edef68 --- .classpath | 2 +- build.gradle | 2 +- build.xml | 5 +++-- maven/poi-ooxml-schemas.pom | 2 +- sonar/pom.xml | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.classpath b/.classpath index 89d75f475b..44a0ea8cb5 100644 --- a/.classpath +++ b/.classpath @@ -18,7 +18,7 @@ - + diff --git a/build.gradle b/build.gradle index 73dfa4b4aa..4a0f9a6e6f 100644 --- a/build.gradle +++ b/build.gradle @@ -225,7 +225,7 @@ project('ooxml') { compileJava.dependsOn 'ant-compile-ooxml-xsds' dependencies { - compile 'org.apache.xmlbeans:xmlbeans:3.0.1' + compile 'org.apache.xmlbeans:xmlbeans:3.0.2' compile 'org.apache.commons:commons-collections4:4.2' compile 'org.apache.commons:commons-math3:3.6.1' compile 'org.apache.commons:commons-compress:1.18' diff --git a/build.xml b/build.xml index c55aff3047..afb9169a09 100644 --- a/build.xml +++ b/build.xml @@ -213,9 +213,9 @@ under the License. - + + value="https://repository.apache.org/content/repositories/releases/org/apache/xmlbeans/xmlbeans/3.0.2/xmlbeans-3.0.2.jar"/> @@ -671,6 +671,7 @@ under the License. + diff --git a/maven/poi-ooxml-schemas.pom b/maven/poi-ooxml-schemas.pom index bff66dfb6e..93351edc3d 100644 --- a/maven/poi-ooxml-schemas.pom +++ b/maven/poi-ooxml-schemas.pom @@ -62,7 +62,7 @@ org.apache.xmlbeans xmlbeans - 3.0.1 + 3.0.2 diff --git a/sonar/pom.xml b/sonar/pom.xml index 9a184a138a..0316403a05 100644 --- a/sonar/pom.xml +++ b/sonar/pom.xml @@ -72,7 +72,7 @@ true - 3.0.1 + 3.0.2 4.12 2.5.1 2.13.0 From b5653e882986e219b3f2c0faf7b73c2d16f11f41 Mon Sep 17 00:00:00 2001 From: Tim Allison Date: Mon, 29 Oct 2018 15:48:44 +0000 Subject: [PATCH 19/33] bug 62624 -- fix npe by adding processing of module name mapping in VBAMacroReader git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845138 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/poifs/macros/VBAMacroReader.java | 159 ++++++++++++++++-- .../poi/poifs/macros/TestVBAMacroReader.java | 14 ++ test-data/spreadsheet/62624.bin | Bin 0 -> 113152 bytes 3 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 test-data/spreadsheet/62624.bin diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java index c3a1c89e78..cb6f3b91f3 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,8 @@ import org.apache.poi.util.StringUtil; * @since 3.15-beta2 */ public class VBAMacroReader implements Closeable { + private static final POILogger LOGGER = POILogFactory.getLogger(VBAMacroReader.class); + protected static final String VBA_PROJECT_OOXML = "vbaProject.bin"; protected static final String VBA_PROJECT_POIFS = "VBA"; @@ -111,8 +125,13 @@ public class VBAMacroReader implements Closeable { public Map readMacroModules() throws IOException { final ModuleMap modules = new ModuleMap(); + //ascii -> unicode mapping for module names + //preserve insertion order + final Map moduleNameMap = new LinkedHashMap<>(); + findMacros(fs.getRoot(), modules); - findProjectProperties(fs.getRoot(), modules); + findModuleNameMap(fs.getRoot(), moduleNameMap, modules); + findProjectProperties(fs.getRoot(), moduleNameMap, modules); Map moduleSources = new HashMap<>(); for (Map.Entry entry : modules.entrySet()) { @@ -327,16 +346,33 @@ public class VBAMacroReader implements Closeable { } } - protected void findProjectProperties(DirectoryNode node, ModuleMap modules) throws IOException { + protected void findProjectProperties(DirectoryNode node, Map 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); + readProjectProperties(dis, moduleNameMap, modules); } else { for (Entry child : node) { if (child instanceof DirectoryNode) { - findProjectProperties((DirectoryNode)child, modules); + findProjectProperties((DirectoryNode)child, moduleNameMap, modules); + } + } + + } + } + } + + protected void findModuleNameMap(DirectoryNode node, Map moduleNameMap, ModuleMap modules) throws IOException { + for (Entry entry : node) { + if ("projectwm".equalsIgnoreCase(entry.getName())) { + DocumentNode document = (DocumentNode)entry; + DocumentInputStream dis = new DocumentInputStream(document); + readNameMapRecords(dis, moduleNameMap, modules.charset); + } else { + for (Entry child : node) { + if (child instanceof DirectoryNode) { + findModuleNameMap((DirectoryNode)child, moduleNameMap, modules); } } @@ -559,6 +595,75 @@ public class VBAMacroReader implements Closeable { return new ASCIIUnicodeStringPair(ascii, unicode); } + private static void readNameMapRecords(InputStream is, Map moduleNames, Charset charset) throws IOException { + //see 2.3.3 PROJECTwm Stream: Module Name Information + //multibytecharstring + String mbcs = null; + String unicode = null; + do { + try { + mbcs = readMBCS(is, charset); + } catch (EOFException e) { + return; + } + if (mbcs == null) { + return; + } + try { + unicode = readUnicode(is); + } catch (EOFException e) { + return; + } + if (mbcs != null && unicode != null) { + moduleNames.put(mbcs, unicode); + } + } while (mbcs != null && unicode != null); + } + + private static String readUnicode(InputStream is) throws IOException { + //reads null-terminated unicode string + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int b0 = is.read(); + int b1 = is.read(); + + while ((b0 + b1) != 0) { + if (b0 == -1 || b1 == -1) { + throw new EOFException(); + } + + bos.write(b0); + bos.write(b1); + b0 = is.read(); + b1 = is.read(); + } + return new String (bos.toByteArray(), StandardCharsets.UTF_16LE); + } + + //returns a string if any bytes are read or null if two 0x00 are read + private static String readMBCS(InputStream is, Charset charset) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int len = 0; + int b = is.read(); + while (b != 0) { + ++len; + if (b == -1) { + throw new EOFException(); + } + bos.write(b); + b = is.read(); + } + if (len == 0) { + b = is.read(); + if (b == -1) { + throw new EOFException(); + } + if (b != 0) { + LOGGER.log(POILogger.WARN, "expected two 0x00 at end of module name map"); + } + return null; + } + return new String(bos.toByteArray(), charset); + } /** * Read length bytes of MBCS (multi-byte character set) characters from the stream @@ -579,7 +684,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 moduleNameMap, ModuleMap modules) throws IOException { InputStreamReader reader = new InputStreamReader(dis, modules.charset); StringBuilder builder = new StringBuilder(); char[] buffer = new char[512]; @@ -588,6 +694,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,22 +704,40 @@ 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 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); diff --git a/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java b/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java index b4885f1d23..673f82e64f 100644 --- a/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java +++ b/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java @@ -286,4 +286,18 @@ 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 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(); + } } diff --git a/test-data/spreadsheet/62624.bin b/test-data/spreadsheet/62624.bin new file mode 100644 index 0000000000000000000000000000000000000000..9af59522e56e77d897ef90d3406004eb508bf3f7 GIT binary patch literal 113152 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%0ApK$upJPH^X7 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 FzW|TSiu3>g literal 0 HcmV?d00001 From 5e1aec11468fbdf0d0d203fa06f7ba7db5b66576 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Tue, 30 Oct 2018 11:00:35 +0000 Subject: [PATCH 20/33] Update for latest/dev javadocs at /apidocs/dev/ git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845222 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.xml b/build.xml index afb9169a09..24b9ade7ca 100644 --- a/build.xml +++ b/build.xml @@ -289,7 +289,7 @@ under the License. - + @@ -1818,7 +1818,7 @@ under the License. - + Date: Tue, 30 Oct 2018 11:55:11 +0000 Subject: [PATCH 21/33] More javadoc updates git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845227 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 24b9ade7ca..f5984cec09 100644 --- a/build.xml +++ b/build.xml @@ -2036,7 +2036,7 @@ under the License. - + From 5ecbaf556343c3d1fb9d70eda5a27e6a62afab36 Mon Sep 17 00:00:00 2001 From: Tim Allison Date: Tue, 30 Oct 2018 13:25:20 +0000 Subject: [PATCH 22/33] bug 62625 -- add special handling for REFERENCE_NAME record that may only contain an ascii string, against the spec in VBAMacroReader git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845238 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/poifs/macros/VBAMacroReader.java | 55 ++++++++++++++++-- .../poi/poifs/macros/TestVBAMacroReader.java | 17 ++++++ test-data/spreadsheet/62625.bin | Bin 0 -> 52736 bytes 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 test-data/spreadsheet/62625.bin diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java index cb6f3b91f3..b44e15c878 100644 --- a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java +++ b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java @@ -29,6 +29,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.PushbackInputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.HashMap; @@ -462,10 +463,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() { @@ -475,6 +484,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 { @@ -521,7 +534,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(); @@ -582,13 +615,27 @@ 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); diff --git a/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java b/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java index 673f82e64f..c6853f156c 100644 --- a/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java +++ b/src/testcases/org/apache/poi/poifs/macros/TestVBAMacroReader.java @@ -300,4 +300,21 @@ public class TestVBAMacroReader { 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 macros = r.readMacroModules(); + assertEquals(20, macros.size()); + r.close(); + } } diff --git a/test-data/spreadsheet/62625.bin b/test-data/spreadsheet/62625.bin new file mode 100644 index 0000000000000000000000000000000000000000..c7523117f71412acfb5960563b0095495dd493fc GIT binary patch literal 52736 zcmeHw31C#!x&N6vlWjsm!lFP3i~$X@#JRI&W+0kmk`R_aAZ&3d8Il1aLnbB@#g!4z z;!;JaPg?5(sUlUYxD|i0!4rmI1MlZGGGC0KowvI z96&MP0?Gk5Fd3KvlmMp#X8@%@8Q=k`feN4!mc-`FVkqs0VCrucNji$(1TM0BWEMPI4E&-7u{E&{=10j>J?^W?LHyL_Qh?sZKM1^~ zN+Iz6T*5V7Qj$mxwSVdps1Fd^Keb_M|I`nN?VsxIq1r$7CDc!7?VrM!a2?PM&#pgw{6fJE(|`UPso zV*95)f!hB9Ky3dMpVt1#P5lGe4X~;GQ$NxOGyy(fDX|afvC>sE>{d)#bh& ztSlI8YhKb3WIoe^^2M{fE8!?H70g?XXWGI*+lnQD!1@&hM&tT&Z@aXfnJ+e*%@(tB zyX>08a?JX44;!*p8l)R*HrpJR4!Edvsz0#A>(}k6OKNZq){q2%3bBU+T=M8!d zOZ-Dd)wcPXeASJ9FH4=)#C|!?(AF`av~pEz0E~n4=J{5(`n{o`ufo^Xe7-bBcSC7M zdoZxF`69ice2uw#u~7e`qyA@{+W%aOLZBQy4cBXdp)vi>swc@;B^{Oi2TAJL@Y>s( z{^#ga95Gh96Rlt2cSXKB9d*Uzq7IjAdeX!jqW9O3EUmGT47yGxOL@Mqxxc-vR>%JT zYTqX%ZQnHk=!|}!rN9_zKg&@u>G`d-p8&3x;ZgcEJx*aL8~4|I1l=a4{fPP}jivf( zNdFv-Kw9bbCOq-4rzbp24f|Zxe*?-`j#GR8OYv)Qliiz;r2l?7gma4V_gDX>?ugWq z-o}4Ac;8Ot@h`?munr^0TG%vh?W@84NAkY6@zZ$pRyCsUTz~ujqW`Q#?q&`eAyJ8t zq0Hv4Xbp|0)Adkg{$w4LFv}^TKRq~gN)skYQA0fOhJ52`@z=!D+x}N#rQkk7{qIm# zaCv%kQZd9Q7^(3vxQD@_M-REFk&^mJZfYx~s@nuxQ8!ika-v_JZlJKrV@h?eSLsIB zTU7Tb*gI7B2-tg7H}wSvRQFie>6AB@=NM?9X;$3n29ndDx=(<+RdrLm>)|%wzNl_* zAv)4VhO%9B)3r{w104TlP#9Pjw-h^KsVG`@YwXZJ2B`l_qH?!Oieyhlea0*E=nOe>1?jUZnru-mY3Km zl8KE)_{XV|DdA1f??~p!Yy)UK?t@CYB<7Xx0qq`Knb>%6Oq3){6-Ltn54MdNFol>7 z4vX{>GeedGwoQ7Ny~Lc#uUnB{URoqk);tIXNs>P^ojXTv?XfPJ0mnxMkHDE+pDK)5+ zS0UEY$h0anWI`Krn=rq*+{b*u_HhNMMU1LZ!Kp=cr9Qu}F=*6{oXfl|%Y5Ss$^s2a zUCPYUpVAx(v=n&)trto~x|A7e<^E1@%Zhf>VrjN+L#MlhhCZR}b$dp&hnB2KUz2lHho>XB`SOkIeci_!b6@nx zjn9;4-qakbGKH=*8mV{amm&QvmBv4vsHyv4KZGVjb?Osb|A2HibbcTy*kAMfRPxp1 znoOek7X$pcZ?Arpx-qkPWvjPs5$Z+Sc71gbd%8B%7HF)%kWY64o7QrEV1;jSReDED zqi((F>=wDyR$wed6n*l|Q$s~{N;Q&3==8>Bsxy8c^R{^cEkR$4KiDqijXcXCjWwJ# ztJ7k4&#^cQi`L~C>q2?;JMuhMv%9+9YOh*qv0c5?y2a(5Z*|?fbWvxPRDU3Arp@f0 zWxLDzN#2~!toaABB4nGp-j=rdoGb3Udc#axvAh0SyE(IL-BojK#hoeVNw<50zBT98 z-&>J2tJ5yK=iB8OL%OoAMrg?N*sZ2vg_|4B@c;Y**?HC68b_9>0;MOtJVg}i@t7M?dW_h|H@2PX!z>Wt{Ws{jPIT;n-@=;lve94WfH_nm|m@V%4rEK$w4VRp;>hDvt7u}$*->hGA$zpp*|Do@7=+@RlU+^pZMfB&B+?=^;MWiws;?9_K<^OgeHe8#-Z`XB2HcNdJzCxY05Pjb7a}JC9g={9oVeDMNhHF*KgL#n+6U`ALfzeCvq;cgsaa;v!%V5^P8Nz z=gkhvmJMF{&6_u8{Ck#s>039;;hQ(>Z_;nh*qmW{zH)*`)?F>zSC7~{@bk?BuixP= z`SHNnQ$M};zK6zaeJF41T-k0~EZYapt{H6{Vl=osvfXvijTkT4J$U!v*^ie#?wlk$ zu3mHAU7co?a3DV+*2Q@@K0iR_tTsC59+Fp zu2B?B<}gw!=ys$j>1y{WO`+LgPUF7Oc@pZ5iKf$q;L02{SJCs~YycFWQgWzDP6 z>8jfVn<}wJH>+;4DD4{ENdycNXoyj&x+xqfd>UOFRuDZT&^M@VqHh=Iy9D}Pf!-z1 z4+?ZX#7H-g922r!8$Qr*gVL(gmBN*1m@u4~+I+!4TZ?zGuQAYqxz)x%yD(g!NV@0; zW+#Ru9-FAxmtvu8$f%vDc;+G-BZYe$n4w1t(*V)D9t+cSGBwD8d=%W1mT|? z;#u1^KZFtxfd|ZGSgBO%3#~Nsp|obFDKl-P6>I&T89dYO6t)ylt)p$f?v_~A~-V#m3NyzEF~7zP|C?E-a)G} z1-1@~On1G4_{M=>Yd5MG#S&#`Zt~FF$!txA=HF8oQD1md3)ml<)@fUNoJXYjJgQCF z1D|f2++--J8lAch?Sb!9-88pXtGcQ0TCTcDSy-jIhr!;Ux`}7IK;I?M_X_kbfqqbR zlT1w!Aekm=WKc{bSQPr3x2g91+ubr0&jm{hlt zR|36Spf?EgR)M}=byHlM1fCrN&vOFLK7r?ez>{u>re~Pyru3LqH_0y*cowN{%EMNH zf4#u7Nuci#=$!(6pFsaWpetP`@`vKhp>9;ot8uEE(pIXvY5u!bptlPAA%VV0;NLFL zpA+c&1o}aNo=#6Vq=Dicr@AR#r$ApM(3cDJ^#Xm9K;Nah=^mdGc)A20HsBVgDi@>u>;Mph8KM?5Y={#NvJ4|&`yk^x+c~UFzGzj!n z0)4&erm$NCo=$)o~Fd3Ozb90kFtBz(+hdDYjokE<(*atz6tkWSQD4- zV$hZPIJX$}-q}-nR%YFU<{uY65`UEw_wBSKyKh9Jwi_qvi!)h$R@l&Py2TR-KU0~_ zj?G`HFEd?HT2J+5rpu1maK|CjIT!KNC+DMQQVQk!Oc&~}D_U1$>tAo~$A$DH%?~x6 z3^o&!@H7V>TS83nkJfj7O>3vME=<#dUF)(}PfsdlIsggV0=}{I`Ms)&%ixROl41N2Ujk zY>&-P9xf{9PVir!Kn_n&l#j}j__{rRLBB&uHx_v)f90O`Murw24lV zG#XuN?!-26u)|*<j%|$Wc5LJE*kc<{Cq1^Y?NG-yaxd@s*aj~y{PwtiX*^~Z_5f5B2>Wa1U>c+6 z^@Bd{Uy3Az&-l{h94Q4*XCf6Yw(QlxsSqyC+57(ISl+*|3U^KW7cPgr23QNM1FitB z1bzst2d)CH2Cf0F1%3ov2V4)(6!;Cmjlf3W$G}a%e*rfGn}DAHKLu_9eg@Flvj2v? zS#{s0+CPVVJ8%cE4Y(874*UYR3%DD&2lyp$FK{2Q1Gpb}0C*4}*}sCl6L=Wd1YzE7ZkAkZ=270Cx} z35)V=oIrP~Zlc!;^ag>x3T|x*Us=LJeIP@1gN#l`i&=@L^M^$D3J{ z2Bp@LoG*3~A~|2|L|l|Fx<`uc-y_FPq$KByop_7##odP`c1a*PU!r`b@(}A4(ib-| z7L{jZG38-Nmd~+#jh00DN9AjdEniEr{Ikc!n=JpLd^Aneii$}slX_#`LWfaRMT24} zZKBXdP_mW&8Xn|6ng-9~eRgZ*lzt}T5% z$D$>|FrmR31s(%MDdI5wKriNN)W%jHvtpyQ2be- z=K{UG|7XFg-T$HXFGlr#ik#|hbpN6w!e) zpAsa3;^~DU)eHL5A?6Qw^X)+#Zv)5YFX{azw*KSRB<*0QO%HFsj0 zR|p!__uCwE@1Hru{H66+nZNAA{^>EHW{-^cOylSRj$ zKZoCUL?j%4{_t8G$q?0{)qIUk*P1)A%|jd?fAjahD*7*`>A!dS?+fY2`=7)OpkLJE z{ZHb4g?f|Y^M5^7sYC0(#oxpJ=MC)T`d+O+(Cho@GnQ7y;w~BY}Ki6fhbfIVZs`0LB1gfs=t#fI?s# za4Ik!m;g)!CILmjX#njtk^z!qg>3`ufCDH7oPY~(1CxO%KnZX{2Ov3> zu&aQn0PV<_222NL05gFaU=}bNs0HQ#bAdWw9xxwR04xL+0rkL{z*)fAz&XGVfW<%q z-~}k&M%YcNd#P$KhusXE3#k2Id-2vtvYcZW3{4w9@+U{6i`IxCGkC%l#+ns`{&i~A1L~-^LzOGITQPP zzgN$no@@2D?R)I@KklY)yZ;gU9MPS)OpfODJ^gr7CtE}up-opYxX$h<{cZPpP>7 z&eEI2$MN(h{w0Y2R#i?P=YMZ~_xSTitnP>DK6al!$JZYZv*-^uIObpOOuA7U&mU8Z zY{mnqtK2KKORf9>X@fa(fHaK_k+cJ(mk+o|{q9$1PM!AMFGIU8lOG^W_iEq$+Thc^ z`*mmfJng$*U{t^R#qi>E<-1>cX^eJ&H1(py-~H0Mu|D=cxtovoKZjd$gz(31|1;qD z{wtvzkAkxo{SO^rKNk9*q~G88Ck6pe$09@gXeSeC{IEP4wjgJtWXK2=wg& zeV0JrE6}?H`aywCYa3d;G#=H?Dbt#QMyL6IjZSm<8lC3dwR5;3qIWxoy8+Zh=WvHb z&*6GkE@}3)1o3?$WV6Z6HMtGur0`l7_?i#=Dlwdm(b_+rnc?ksv(<&&9q zK9tU1QZGU6t5vlVeG4E<;ZW+E%B8i)qhX^z*_BkSSI=MKU`6b=0C-(us7&-}FQZgA z@N8bIGVx7GI)q7QFd4n!UzTHD@n4km75^7BP#r6f#-vWR%rBg58Q``Sy5#}+wqF(J z+l%uZh50|~C@#*w(s^fgXc`@zEWLvD_3R|FoAvelq+UHgiLVCZkBk{O-1C!Czt2w| z>ik^v>ztIr`)j_&J5XZu7jgcnzw=*o9FO{b zYA>)3X?#e_GL=i5f9uZ~Ogevxv#eHc`s>squfOK>sJ~Jll0$dV%lGem ztNLpw1D!X|aZT6kQm=Ko18^nMFNa3;%esK_@k{=J#)Ww5P-rq;%CD+l#=W53?Re-1E4FYx8`JbHx{iNuN6u?!nmC^|jxSaZy}kVX9r1jmzYRlLseVzZkex_> z<39gE`r93c(%*0}EmnWyWmT#uZ-oPDwa;prp}V1X);^f&v6z)NXqfm%SE0Kx^*gA3 zrhK0XZ_vn5zRz?XzOnTsUZIidmJ8i@mC`w>?(vpFw{<{=wJ6_MnGSU=v{5;|&n`y< zYXI5{v`)3Jg#ANcJ#ZCpHE<1ZE$}1YI^cSMMmaYCHv$`h9|Jc5{{`F(Yyy4){1mta z_!&U){5R~)s{1z8{yFU1fjfY0z@5N$;1|GMz}>(-z%PM&f%||R!2Q4jz=HtE{uS(< zz{9{U;D3N$1CIcY0*?WY1G|Av;0fSKfWkfv`x)R_;D3SVfaihV051T0fZqbY16~C7 z0xtnC1Fr!82mBuR1Mn)a5BMYS8t^*s2Jk1~&%m2N7w{H9alZrmUDf>_?Dv5WfDeI> zfR6#<`z!2EfKPz~z~6w+fX{(1fWHG@0$%|KfvZuTx!q1w<7*T`gcF?U)c$Wi4P?mJ;%&_w;+CP{1e^((4Wue`oiof0MQ+>fhridxW{w7`didwnL6!Rcw+Vs(_{GV0X%PUcZRY0CIp=RERxx&Ynf zTvJ9?CNO=~pHfu)$q;GxW=K8U`AfsMrawjdc)EGgn4lT!=GJIG2Twh~wYYM|4rBoV{DjCl#k&J0ln{G&Y>Yxmr zG>E@Re89=l^eK|@1g2|i4~CioeqR>b$_7iDn11}()fb!1A+yD7v*I%j>G=kOhZ&`8 z-5|3W4dK!;mLl^QGaG{`yXKZ)fVD3-Radyn@J(NTbE7xd9AGW&?waPtHh2Y=22Hb< zE@jP)K9j{P%aU84>7I9CtIxz{HZN)Owl(`$ySpyvZE5ndwkFf;nUxk(hAz8(3`;SU zbp$U7tXxxwkKgDPvjzC-bs5HCwQMe%&8D+Tyt8kf*5D3lkLbtvV*#WNJ&}uUV$H1W zkg+6ZNbg7d58?5#TOEUo<4<}L_to@g{QDH*zmc9WFk<}u*#S@MKiHGRC_AYmAeo0U zZzKM@R3xE ziT?hzrbU;WTR!%|;jjGVt<$Hkrg({7mD+ZV<+`$GfBEa__l|zcQ>oF(HiKc=A&llrrUrt66hq~zMxQ@4 zA(!b53(CvBdOMe;7#23S$c`s2Q=L{z+G_GfBYNTugMg z2dQ~(Y#=w<5}MM`j$AgtFmHKt`@%rmiY0--ic8+fWf>u4tzO3#SDx?ncVLg}us`Oq zG(%0Gsl)HHH0DMx5Ao_t0h^ zdsw}Vc3<#=yKoz<*4rNR)wTL)8Q}356t_aTH6@p2L4p#$N_*+FN3I)2)d#z#TjT@C3>2<%2abF}n&XdbV2QZpSK-!DWE~--$odDQC^wR#$d01= zXQKgZc|ri&s|b)C3BZ>fW@bmZ?IQ(l`#x~zM7Xtq?(ZK(<{PvIvI04mpi&sjA2Hq%Tpy<)B!B0Y$ z6AmwvGEse-r2#1Q6;c*z;7TbQ^)et0L^)=I3F@Mx$M0b94`A(54jYJ}whq$?!{LkC zrcBNFBqLrFb)l4tBSWjDL2M}dp)?pnC!U5Q`&)+eoYDu$b0k9?|3WE+6`^CNi9sD( zPCe{#b36x*Y!0lgUwcxPjXe@dzdxkAyxvm5s+<*On`e@pl~4jsmFP3dxMP}3NCA!yB28HS?nQox;$q+X9N!@-! z&G0qLl;&Ww53-`R|p7O;i*nr2%qz6QCZQy1vejTrj7Ov`JTBRdD- z?F%X8I$65n5j^}yT~jXUQYJomQrVb-^7MiU<@n@cTBoiiz?4VuRFl) zxy6STGa@71l9OIJJ3p^Bf7O#Gg-(FNGdo|#|8)Ab$+!x4?)k0zqpF)~^FQx&2deTv zXHU7m2(YUBo2B&1{EYP3`R~4-JA2XC!SnM^nfOEF-KR|4nr*o++tQk!YPxv%%KYc2 z-kn{x9{t-1Ev9_$HMLzXe8}?Rts|@SmM5>9s{b@ku6||b>E`OK;~hTN`+4}e>Fqqn zr+JP8c`s+&H~#iHBRZ#GqMV@8onv9W|JdvuypTTMn&q63H)k5 zWb;2$Utacrr*%sBm1Pfm?w=w_4|w)Y87M7Id+)|l<70EKD|p1SFwI=DyC&tll7(p- zO33TRTI+}Bq<#J9;VI{8gsoW}n3)E~}DbL4d{o1AysvR8Gt z$tlP17`TDY=yX_y0Eu2yGQyreVcryzIYB`1EPt#WP02{*en64BbW#soE z{lMrjdnNjX!+Y?`T*@+%L=tlfkvi4TxLCsK`*?OXt7aEtrzQQ-yOwPjB~~5A7c0kL z-lqt1#xNI9!@R5!{Xq*0qC03u-(W&~70iusDMr6&)6MYr!nYazE#Pn?4AI&Y{s3DF zKNCi;OYui^AFe2!GXC5#bT`Jk&5+Omd46z}!){l?m=G475opO`Edoay`05FA_`@TC+{s`*%(Qo4kl3mSHCAikN1e~BWwja`WRNS;D` zCc|3**{27ZcgNf^{~p#r!#va^EJ2ooQU({jp+LsfB%lJZ z+0}Yn24CuzZ204J3+9ynb8|1Ym+DVl?Jl8jwRH%3by{_%KXOUk{xm-WGvINM3ZzRq z%vI{tffRq%(l4L&%)n=?GMQ0%2Bis4m@Ev&24byz5T3*|p4Xl|IuySiv4){m5RI#l@LPTo^h9 zPu=Hg>H?_#lsa1iai=TdM9y${EIutpM)}m?>ri6i#qpc1iW651zFBeNCQbTqgu_Nm8f%|#r<6J3L{YGX7;{It!r@m==VDfom}n71lkQM%virw3!{J47sd7L( zeNdRp1`_u&v?noDRz;LEL8=xDsj?yreOHLg_Gqf2$+jtCTnWT@1u-^=852^Zu+Btc z?1~t70x^w(7(2w!TmH!?AC%;I6ip4!pUDZt(Da6u)s#2%vSu=LqCPdUTvyYJTE%Z{1X%L|)2v@jBj3H&2Z#o=%-nJncwb z4})x~U5VsT3#A?`k)KAx!@Ea+jW6D&1`x{F^@# zA8865mfMEB-Zb@pfBJ0d#w=_05{>@G$!h?c$r2IKIOS`^#e6|cdT717yRsRmumD8FWi0hlMAa;)-@ey{$kHV*J$+R z=jXf>{-S)(ts|cKc#8g^T6z*vdiGZ2Z+`By@=aSyx4r-BsCglc-}By}Eos-5-*VG$ zhR#0tvj?|p^k43&IFY?qc3P-m@~o9({#>ZhS6+2*@zej9_S%k1gP-}m8;do1hwHtj zr-#8Yl3iOvl*!CX}d>pn*i` zKUp{(YD_Y};XU}tJ@`%Q!OyGu=^|wz#pGHmPmWS9I_46};=*nr(_=#JiV69l5b}m@ zAq_DhY2y|dstY=Hl@Rh5kvoZw6o`wsU22IMSO$8DMCP_6^l6yYN@U(jLPzl_>3}$> z9(bE?WZ4)2@BqVmeuV{Muwv;7?*-ko zD~-y83yE7zqRnv$C()EJzAJ!a^8}>+m||8}vLh?KxWlXg7-L5_B@%0nz^bj< z5}oF|H4)LpaKs25FY3crg^5bvmDTu_l$xVdU#bnf9?3X8z8Y73G>Om8Bb(G` z2hV=WDl#OTyQ05*#+=VOaCc@mo+x+J#d-B=JQ=FZXVGz^=~NOXqij2`$NLidf~1ob zTK6Ew%pk~!Kyx|mUghEIAiN_Y9U>_S%Vt0LA zfH}Ip8JbIq<|!|sLrK_=Hb`gl$i&9aX?^*n{ZAxnEI{e#P1wI6z$;X|N67T`{*O?! z5iy4oe~Ud*Bk1@O)e%R8;!r8Y5ej{#i+G$73T@dXiYr2)*BB5*Hb+SO?Xn6{7KqUr zZH=kXR9krqu*J}+&T_gvhE6HxbVm$bOTTOpBBkaMHPb3e3a~5FSy^nav`n%Ud&(x+ zTos;4Wj3pAQkA7bt|%|7wA&qyOAE4$6@kW%mDqq(BDVU|PN_DlCd`axZ6cFIRFe+1!Jl9OI|ur;=aRxcsuk zUEAZX9r5{TiIF|ayV6%u5LNF`z|}r~tFp;#Rgfx%86;25)RF>6rNiU0Spci2qReI% zLG_}xyriJqSzczdl-q4q4$!B1Aj4udS2@eE=eJxYlzZTjWjw1o&>l2ZuEKt{CSRLr zTFcVFIa$U?Rgz0CwmPkr3Wv=+$y4PrPm*O%)g))7%{s|!E;D;vvTQcn%PyU~puBQ& zB$Qc^Ok1 Date: Tue, 30 Oct 2018 15:45:00 +0000 Subject: [PATCH 23/33] bug 62625 -- clean up imports git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845246 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/poi/poifs/macros/VBAMacroReader.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java index b44e15c878..1a971b7e1b 100644 --- a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java +++ b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java @@ -29,7 +29,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.PushbackInputStream; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.HashMap; From a29586e45edc69ea421f782cefa0db86be953072 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Tue, 30 Oct 2018 16:39:33 +0000 Subject: [PATCH 24/33] List additional dependencies from the Ant build file in Eclipse git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845255 13f79535-47bb-0310-9956-ffa450edef68 --- .classpath | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.classpath b/.classpath index 44a0ea8cb5..247c5000e2 100644 --- a/.classpath +++ b/.classpath @@ -30,6 +30,9 @@ + + + From c956084b0d7c807f21c93f4a82571496d4bb65b1 Mon Sep 17 00:00:00 2001 From: Tim Allison Date: Tue, 30 Oct 2018 21:30:57 +0000 Subject: [PATCH 25/33] bug 62624 -- fix recursion git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845284 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/poifs/macros/VBAMacroReader.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java index 1a971b7e1b..4a9f258087 100644 --- a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java +++ b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java @@ -352,13 +352,8 @@ public class VBAMacroReader implements Closeable { DocumentNode document = (DocumentNode)entry; DocumentInputStream dis = new DocumentInputStream(document); readProjectProperties(dis, moduleNameMap, modules); - } else { - for (Entry child : node) { - if (child instanceof DirectoryNode) { - findProjectProperties((DirectoryNode)child, moduleNameMap, modules); - } - } - + } else if (entry instanceof DirectoryNode) { + findProjectProperties((DirectoryNode)entry, moduleNameMap, modules); } } } @@ -369,13 +364,8 @@ public class VBAMacroReader implements Closeable { DocumentNode document = (DocumentNode)entry; DocumentInputStream dis = new DocumentInputStream(document); readNameMapRecords(dis, moduleNameMap, modules.charset); - } else { - for (Entry child : node) { - if (child instanceof DirectoryNode) { - findModuleNameMap((DirectoryNode)child, moduleNameMap, modules); - } - } - + } else if (entry.isDirectoryEntry()) { + findModuleNameMap((DirectoryNode)entry, moduleNameMap, modules); } } } From f695914e89db89e1d76c494ca0928bc9bee172d6 Mon Sep 17 00:00:00 2001 From: Tim Allison Date: Wed, 31 Oct 2018 00:47:51 +0000 Subject: [PATCH 26/33] bug 62624 -- fix loop identified as dodgy by FindBugs; add a other sanity checks. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845299 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/poifs/macros/VBAMacroReader.java | 60 +++++++++++-------- src/java/org/apache/poi/util/IOUtils.java | 16 +++++ 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java index 4a9f258087..6c112c213e 100644 --- a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java +++ b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java @@ -631,14 +631,19 @@ public class VBAMacroReader implements Closeable { return new ASCIIUnicodeStringPair(ascii, unicode); } - private static void readNameMapRecords(InputStream is, Map moduleNames, Charset charset) throws IOException { + private static void readNameMapRecords(InputStream is, + Map moduleNames, Charset charset) throws IOException { //see 2.3.3 PROJECTwm Stream: Module Name Information //multibytecharstring String mbcs = null; String unicode = null; - do { + //arbitrary sanity thresholds + final int maxNameRecords = 10000; + final int maxNameLength = 20000; + int records = 0; + while (++records < maxNameRecords) { try { - mbcs = readMBCS(is, charset); + mbcs = readMBCS(is, charset, maxNameLength); } catch (EOFException e) { return; } @@ -646,53 +651,56 @@ public class VBAMacroReader implements Closeable { return; } try { - unicode = readUnicode(is); + unicode = readUnicode(is, maxNameLength); } catch (EOFException e) { return; } - if (mbcs != null && unicode != null) { + if (unicode != null) { moduleNames.put(mbcs, unicode); } - } while (mbcs != null && unicode != null); + } + if (records >= maxNameRecords) { + LOGGER.log(POILogger.WARN, "Hit max name records to read ("+maxNameRecords+"). Stopped early."); + } } - private static String readUnicode(InputStream is) throws IOException { + private static String readUnicode(InputStream is, int maxNameLength) throws IOException { //reads null-terminated unicode string ByteArrayOutputStream bos = new ByteArrayOutputStream(); - int b0 = is.read(); - int b1 = is.read(); + int b0 = IOUtils.readByte(is); + int b1 = IOUtils.readByte(is); - while ((b0 + b1) != 0) { - if (b0 == -1 || b1 == -1) { - throw new EOFException(); - } + int read = 2; + while ((b0 + b1) != 0 && read < maxNameLength) { bos.write(b0); bos.write(b1); - b0 = is.read(); - b1 = is.read(); + b0 = IOUtils.readByte(is); + b1 = IOUtils.readByte(is); + read += 2; + } + if (read >= maxNameLength) { + LOGGER.log(POILogger.WARN, "stopped reading unicode name after "+read+" bytes"); } return new String (bos.toByteArray(), StandardCharsets.UTF_16LE); } //returns a string if any bytes are read or null if two 0x00 are read - private static String readMBCS(InputStream is, Charset charset) throws IOException { + private static String readMBCS(InputStream is, Charset charset, int maxLength) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); int len = 0; - int b = is.read(); - while (b != 0) { + int b = IOUtils.readByte(is); + while (b > 0 && len < maxLength) { ++len; - if (b == -1) { - throw new EOFException(); - } bos.write(b); - b = is.read(); + b = IOUtils.readByte(is); } + //if b was 0 and the above while loop + //was never entered, check for a second 0, + //which would be the sign that you're at the end + //of the list if (len == 0) { - b = is.read(); - if (b == -1) { - throw new EOFException(); - } + b = IOUtils.readByte(is); if (b != 0) { LOGGER.log(POILogger.WARN, "expected two 0x00 at end of module name map"); } diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java index 839663cda3..009510ffc6 100644 --- a/src/java/org/apache/poi/util/IOUtils.java +++ b/src/java/org/apache/poi/util/IOUtils.java @@ -548,6 +548,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" + From 5585f34da058f4141f3c9cc2b33bd1c6ea17dc51 Mon Sep 17 00:00:00 2001 From: Tim Allison Date: Wed, 31 Oct 2018 00:59:16 +0000 Subject: [PATCH 27/33] bug 62624 -- ensure streams are closed...thanks to LGTM, and PJ! git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845300 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/poifs/macros/VBAMacroReader.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java index 6c112c213e..8ba36e7a95 100644 --- a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java +++ b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java @@ -350,8 +350,9 @@ public class VBAMacroReader implements Closeable { for (Entry entry : node) { if ("project".equalsIgnoreCase(entry.getName())) { DocumentNode document = (DocumentNode)entry; - DocumentInputStream dis = new DocumentInputStream(document); - readProjectProperties(dis, moduleNameMap, modules); + try(DocumentInputStream dis = new DocumentInputStream(document)) { + readProjectProperties(dis, moduleNameMap, modules); + } } else if (entry instanceof DirectoryNode) { findProjectProperties((DirectoryNode)entry, moduleNameMap, modules); } @@ -362,8 +363,9 @@ public class VBAMacroReader implements Closeable { for (Entry entry : node) { if ("projectwm".equalsIgnoreCase(entry.getName())) { DocumentNode document = (DocumentNode)entry; - DocumentInputStream dis = new DocumentInputStream(document); - readNameMapRecords(dis, moduleNameMap, modules.charset); + try(DocumentInputStream dis = new DocumentInputStream(document)) { + readNameMapRecords(dis, moduleNameMap, modules.charset); + } } else if (entry.isDirectoryEntry()) { findModuleNameMap((DirectoryNode)entry, moduleNameMap, modules); } @@ -485,7 +487,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(); @@ -620,7 +622,8 @@ public class VBAMacroReader implements Closeable { if (reserved != reservedByte) { if (throwOnUnexpectedReservedByte) { - throw new IOException("Expected " + Integer.toHexString(reservedByte) + "after name before Unicode name, but found: " + + throw new IOException("Expected " + Integer.toHexString(reservedByte) + + "after name before Unicode name, but found: " + Integer.toHexString(reserved)); } else { return new ASCIIUnicodeStringPair(ascii, reserved); From 4019c8ddf5f82a44f6566ecaf12d0a37b310df60 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Wed, 31 Oct 2018 16:26:52 +0000 Subject: [PATCH 28/33] Allow the forrest task to be called from the site sub-project git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845343 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/build.xml b/build.xml index f5984cec09..d15b45b74b 100644 --- a/build.xml +++ b/build.xml @@ -1799,9 +1799,11 @@ under the License. - - + + @@ -1821,6 +1823,7 @@ under the License. + Date: Thu, 1 Nov 2018 10:29:54 +0000 Subject: [PATCH 29/33] IDE warnings, tried to reproduce Bug 58927, but could not git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845434 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/ss/util/CellReference.java | 14 +-- .../apache/poi/xssf/usermodel/XSSFSheet.java | 117 ++++++++---------- .../xssf/usermodel/helpers/ColumnHelper.java | 4 +- .../usermodel/helpers/TestColumnHelper.java | 49 ++++++-- 4 files changed, 99 insertions(+), 85 deletions(-) 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 */ +@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 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 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 commentsToShift = new TreeMap<>(new Comparator() { - @Override - public int compare(XSSFComment o1, XSSFComment o2) { - int row1 = o1.getRow(); - int row2 = o2.getRow(); + SortedMap 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(); - } + 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 commentsToShift = new TreeMap<>(new Comparator() { - @Override - public int compare(XSSFComment o1, XSSFComment o2) { - int column1 = o1.getColumn(); - int column2 = o2.getColumn(); + SortedMap 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(); - } + 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,7 +4604,7 @@ 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 + * @param cell The cell that is removed * @param evalWb BaseXSSFEvaluationWorkbook in use, if one exists */ protected void onDeleteFormula(XSSFCell cell, BaseXSSFEvaluationWorkbook evalWb){ 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 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/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()); + } } From f539222425ccf07c85a528628cb7b5a1c44c09db Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Thu, 1 Nov 2018 10:30:16 +0000 Subject: [PATCH 30/33] Remove JavaDoc warnings git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845435 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/poi/POIDocument.java | 12 +++++- .../common/usermodel/fonts/FontFamily.java | 8 ++-- .../poi/common/usermodel/fonts/FontPitch.java | 10 ++++- .../poi/extractor/OLE2ExtractorFactory.java | 41 +++++++++++++++---- src/java/org/apache/poi/hpsf/Property.java | 10 +++-- 5 files changed, 63 insertions(+), 18 deletions(-) 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 LOGFONT structure + * + * @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 dirs = new ArrayList<>(); // For anything else not directly held in as a POIFS directory @@ -237,13 +263,12 @@ public final class OLE2ExtractorFactory { ArrayList 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); } From 5416787da5fbb4ef578e943b8de2b21422e0b9f5 Mon Sep 17 00:00:00 2001 From: Tim Allison Date: Thu, 1 Nov 2018 20:31:42 +0000 Subject: [PATCH 31/33] bug 62624 -- further cleanup git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845511 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/poifs/macros/VBAMacroReader.java | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java index 8ba36e7a95..8254b86d8a 100644 --- a/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java +++ b/src/java/org/apache/poi/poifs/macros/VBAMacroReader.java @@ -70,6 +70,8 @@ import org.apache.poi.util.StringUtil; 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"; @@ -352,6 +354,7 @@ public class VBAMacroReader implements Closeable { DocumentNode document = (DocumentNode)entry; try(DocumentInputStream dis = new DocumentInputStream(document)) { readProjectProperties(dis, moduleNameMap, modules); + return; } } else if (entry instanceof DirectoryNode) { findProjectProperties((DirectoryNode)entry, moduleNameMap, modules); @@ -365,6 +368,7 @@ public class VBAMacroReader implements Closeable { 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); @@ -634,47 +638,53 @@ public class VBAMacroReader implements Closeable { return new ASCIIUnicodeStringPair(ascii, unicode); } - private static void readNameMapRecords(InputStream is, + protected void readNameMapRecords(InputStream is, Map moduleNames, Charset charset) throws IOException { //see 2.3.3 PROJECTwm Stream: Module Name Information //multibytecharstring String mbcs = null; String unicode = null; - //arbitrary sanity thresholds + //arbitrary sanity threshold final int maxNameRecords = 10000; - final int maxNameLength = 20000; int records = 0; while (++records < maxNameRecords) { try { - mbcs = readMBCS(is, charset, maxNameLength); + 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; } - if (mbcs == null) { - return; - } + try { - unicode = readUnicode(is, maxNameLength); + unicode = readUnicode(is, MAX_STRING_LENGTH); } catch (EOFException e) { return; } - if (unicode != null) { + 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 maxNameLength) throws IOException { + 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 < maxNameLength) { + while ((b0 + b1) != 0 && read < maxLength) { bos.write(b0); bos.write(b1); @@ -682,33 +692,21 @@ public class VBAMacroReader implements Closeable { b1 = IOUtils.readByte(is); read += 2; } - if (read >= maxNameLength) { + if (read >= maxLength) { LOGGER.log(POILogger.WARN, "stopped reading unicode name after "+read+" bytes"); } return new String (bos.toByteArray(), StandardCharsets.UTF_16LE); } - //returns a string if any bytes are read or null if two 0x00 are read - private static String readMBCS(InputStream is, Charset charset, int maxLength) throws IOException { + private static String readMBCS(int firstByte, InputStream is, Charset charset, int maxLength) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); int len = 0; - int b = IOUtils.readByte(is); + int b = firstByte; while (b > 0 && len < maxLength) { ++len; bos.write(b); b = IOUtils.readByte(is); } - //if b was 0 and the above while loop - //was never entered, check for a second 0, - //which would be the sign that you're at the end - //of the list - if (len == 0) { - b = IOUtils.readByte(is); - if (b != 0) { - LOGGER.log(POILogger.WARN, "expected two 0x00 at end of module name map"); - } - return null; - } return new String(bos.toByteArray(), charset); } @@ -722,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 + @@ -787,10 +785,10 @@ public class VBAMacroReader implements Closeable { } 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); } From 341f456ef7868db6c518cdcb0570dd9c43b41472 Mon Sep 17 00:00:00 2001 From: Tim Allison Date: Thu, 1 Nov 2018 21:14:03 +0000 Subject: [PATCH 32/33] bug 60316 -- until we can implement it properly, gracefully skip the glossary document in XWPF. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845517 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/ooxml/POIXMLDocumentPart.java | 9 +++++++++ .../xwpf/extractor/TestXWPFWordExtractor.java | 11 +++++++++++ test-data/document/60316.dotx | Bin 0 -> 59583 bytes 3 files changed, 20 insertions(+) create mode 100644 test-data/document/60316.dotx 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 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/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/test-data/document/60316.dotx b/test-data/document/60316.dotx new file mode 100644 index 0000000000000000000000000000000000000000..5d24a7848cb65919c3273a4c601c036cdfde9b79 GIT binary patch literal 59583 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 literal 0 HcmV?d00001 From 9aabade3f067b0befbc2680489ace13a6d5fa7e6 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Fri, 2 Nov 2018 13:34:28 +0000 Subject: [PATCH 33/33] Bug 62836: Implementation of Excel TREND function git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1845586 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/ss/formula/eval/FunctionEval.java | 2 +- .../poi/ss/formula/functions/Trend.java | 377 ++++++++++++++++++ .../functions/AllSpreadsheetBasedTests.java | 1 + .../TestTrendFunctionsFromSpreadsheet.java | 31 ++ test-data/spreadsheet/Trend.xls | Bin 0 -> 48128 bytes 5 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 src/java/org/apache/poi/ss/formula/functions/Trend.java create mode 100644 src/testcases/org/apache/poi/ss/formula/functions/TestTrendFunctionsFromSpreadsheet.java create mode 100644 test-data/spreadsheet/Trend.xls 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 8442f5832f..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 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

      + * + * Syntax:
      + * TREND(known_y's, known_x's, new_x's, constant) + * + * + * + *
      known_y's, known_x's, new_x'stypically area references, possibly cell references or scalar values
      constantTRUE or FALSE: + * determines whether the regression line should include an intercept term

      + * If known_x's is not given, it is assumed to be the default array {1, 2, 3, ...} + * of the same size as known_y's.
      + * If new_x's is not given, it is assumed to be the same as known_x's
      + * If constant is omitted, it is assumed to be TRUE + *

      + */ + +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/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/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 data() throws Exception { + return data(TestTrendFunctionsFromSpreadsheet.class, "Trend.xls"); + } +} diff --git a/test-data/spreadsheet/Trend.xls b/test-data/spreadsheet/Trend.xls new file mode 100644 index 0000000000000000000000000000000000000000..8a88709363434ed227c4a64e5d4907c046c7ee92 GIT binary patch literal 48128 zcmeHw2Y6J~_V1oa8we>t2!ucwLIMFo2pvU8LTJ*PqEaWxgalG>l2B9x6s~%02woeg zh;qS(y@SRsiiM`4prWD|5iIw5v0>itxA!@7%ACY-|L?u;zVC7Jos&6h@4fn3yPQ2| z-g~$CXLmi)_K>h0LqwSPqppdFG{If?JVU>a7bvf*tgS#z7b4i+uYN{Bs~Yda1^8-epYcdo2=t-PknYb>rV@sw=y zk%aUTDdGU?d&Fus1Mw(VzVL}MQ7Q^V0N4Mv8cqI$QMV;(^wn#`I@b_l zh)$)R8ZKx|qf$=|w`i^7s8^@W^&0iy5aO&qt&z|`jc%d@R_!0G5o5EhSB!;)=ZX?h zE$y7?a-nuZa)tkCjRWwohH=o}`UZKb&jL;Vq=rj0sh87~@OB~s`WLBbshu)Ab;_RI zbH=3twb!y?)2a>--9&F8 zW|Ng}UY#@Tc1DH-%;-8u1Elsy1>dtXa%Y1|mgtH(_4Yx!dSgl2+_I0j2l{t)0uP<=myML_g_a)hriwxM+yk)C zl9celViU|#PqXP_vha%{F|z>#IpXgynF&D_Xg5UNUY-(uw%80#8$?kbC!xqL6k8At zuGx^d8#Le~HOrJv8m^(^NJafrlRi~@UULPnDdF?Qy`oQjL}$YYstL^yO42B2EtVSu ztz|m772-bRO9vV4>Zp&bb3H5h9He!_hZH7WbtWzr_d|;fGGTBZD!IXeQOlVDYq=u- znMgoR6XK^jul%nrwO+y_PQ(AV>Vdj-!7GD@+otEowUY34R=Pp>L-oL~st0~sJ@7m0 zf!|jT{DFGl_3?94J@xOe2VNh3RiE=3>7PM8sQRIB#ZM?)@e>NK5C0>Q&(QkM)B~@t z{uA}of2tn%WA(tFuLrL9i4D=ugR(y60ZMN-HgA4&^v%&CTBt_{3$hHCOD-A49Qb!z+8Ds z4H%RSHpsKZ1A{B9e)kTSf#NTw%W-zW;oDM=9SKuhBaqMy>qo+T*a#$a&H9lr88!k5 z-L-xsOe~E+LKm(d3A?REAfa2=j|82Rx}R&Pb0p*gCJSs1YSQFV_8asc=T}CT_xuU5F272;X>ajD`4RFO22X^u>k1%z~Hf`Dz!cpY%LWd>UsHVDLHo$De z+CaC&Yu}lD%s#fkvaBhMNRyh)N#7x`;WUD^=s!nNm(wtkK8-dyVpz=*M&oQ2LxtlN2Oh&%u9i!r-V`Uh3v82EZX*DZGinF@7}0Tv|d{q?v<-O=?X zB=%?@7*r2+88V;LeXOo-XAp^=f*yVJ(U52-%pUE8*`u8>(33}~&CUSaY%^pAv(M^G zIpxLwb14XqNKBp=qN&iFHOSJy?y9b(o^x#7!C7b!UjJN%dbfwFIehi*Ae58AK~Csy zP`p*2p$&!v8#sDJXoH+nZIIh=gSGA!SU16Zvv+p7w}&^cu+U#o-D{0!oLIxoT{~Ri zDRnV;P?jyyg%=&6SLBI8{P{(Nd{D`xK!*8M zQ(Bb2xbDZD&SVnoWcr1Y$!dTMv$96U<1gNS=tpNVt?Xp_hmz^t02!5MrP7-Ci$DMF zBWE&+B-6KHPwN0OeH-CvJI8MS;J7oH)^;)up4O=WGG|`7~t$PDx%$~M$Y~5G4I+JN@C*$C0JsTin_OzX2AAjv@XEMol zG7g^BrvWl%Pun^6wa*@RCew~&`Zw%pUXbbE2v6(XXWdI*DKdyA?yL&twDxu~4xX0Q z02#BV_3rbphgUk2NwJf0@U)Bu$e2B?cb^?Qb~uyiU?=0?X_*a>F?(9?K6U$xoymCZ zWE?y#y8$w0PwU<1x_h;_vD>Djos5I0^=*KR+0%OWx%a*6oTO=nhw+$72m8%DTff=9 zE~?(XQi$p6_BkW%WFUn{*^zd%AvJrE5XYAGaz>hFAcg_ntetH&O&9cZq;%# z3_~gqkedBLh+%EA49`@ zd@94AnJY*4(YfMOA~ML8y3@H*cRE+lM?{6U+>@>9IT85Cnm{(J8H}u~grg3!;&rvH3l}4yv`U++a=Amt1!6j`SZqMU zlHDl3uc)+Qt`~C!8>3}rR^JRXQCX#hUO#G+<2GM{46MVEIyR%SGZhf6IN2**BFUR?qt$HUVDGC>fP zrhOMaB0C4;Wgz2ad}XzNjiYOhIw$Im3G>Y55zt+l zm4%217-Ax14g@FuR2f}$5M6a@X>}R2)yvu{EDq+G3h$_f;i#`rZ+8*JSrSEMH9f$# z_7IjNQBdK^`r*>4Eb9x@yPijswE7)gthpGB#3QEp0u@YID!t}d8Ffb^Q%?hh032D* zT25(|ow(HSb)NIYb*4p<)sU_^{-y#+fs}R%SY9GOjf3n=hBffJ-q2#HSSUTnsA1ms z-CxXcaRF(Sh!gLP!v_S&WaL}#IR7dPYQLXI5Y!s*R}|gmX(MhkaiJVptuWq=Z&uuG zDr2*2;MgwwZB^f92ycz28f-&Y8w_S~c(}nqJF5Lv)p-HG4{L1P-yMO!7hN8lLcB1= zEq)gR<0hbL9!>=Dh{`}Y#xK~7Fr~!rulD9wmX~8a=}hc4K>y}1tVR-8aZIo*z}P%u^CbJF zb1NzX&_M?5R$Nm?U3d#?0`T7IvL&EcRkL70Wq`g>;4kqlEUl~wbOq`ORKOx=PnSK< zTj;Am9ok=N6a~`17}R8VZlJPe0rd(6SD|8A<)SR_i|9p_OKG-3+ko^z=^;|(-l0$k zv{F%74acj7QHaj3yR25-kSjk^Rxr8eqEdKTz)v}#y<%T!SxvwXA*RxIwc9H1T*QR0 z0#smwBBeDFszPL7$S=)C-_XRWf>RU&xFl;M<`_YT^m~;*_SN))YHXz25Vyo>;w>l| z9G5{zl-^2If*BAX(p@DR3?uT4=H_CH)&qV&j8P*E*oQc|cYEniUP_m)XdFY_uoQYePL@*hN9sed6X;P+LPf-B^KE(?5B)$}83 zsMJ^1MFoe(+)$E+(X0GrG?nIVU3^s<{x06i1+-umlI<|)q5xR{3B1aOOe(XqR8!S( zMxr%q8Qd@Q1>pI}Ycku(G{QikVTx-iq=QtQ>76=x`1oPKQyG|>%0`Ut)K2n75TBV}T*&Xno^#erv3vx7nGRMeCg_yb^kc=>|rCCWp3 z$kblQnrK~ADie8GDH0;|0?jcSBlhN&cq;>?b4x3x2yqUqSb=3{>GUEqe$JV|V5e$x;K1v}C@8g%@jv z^3E+?=&wi=VjTLQQaXyBbA5B+L+w$3RHgC39Bof9tGR!Y)R~0s$^=Y@GqLZ z7@xaKl`AhaYuzkX6}wWMp)@ECLFiv((}QjVsb_x?9=wInM<7c3g-U5_3>TRKmY`9Y zeE+mQMh*Fu@H!tfDTCCa$VM60#sT{O$$n{@LL?!|ud*9iDL_7h@MbI7T4R?%P@udLZ~2tddu7}; zz#}HQ&OBP(&n8(1*j=SeCh$OMp})#jV}TZf96!nM)BO^T4BPx!2up0)AP|;e!_=wkr0V$XlRZg0x={@CB9(Gm+GS;6Fiw`Is8hU|mS25sF}P)=ck`Og%D# zq4l2-8Y9F7{y?RcWz3QApI{r`P-y)p2;seX7~TYvs2nUHz!WZ%S5&K}wT=D_|-;!X{s;dzu49KFI1!bj$7{~?nBGrLDO-rYPROd#n7X&H` z&<8A$402>z=))M6UQ3mf`2urg#$ta|ReF&>6NBQiQf;Ip!&w_*htkA`PMfxDT%b#n zuDc|}QB^QvVNOpOMb@s(AysISA@xn>2-}?GyR(0h-IEOsw&7YesCqn=1(D*M3T-IN zoM7*UbC%NFfhoR$=6VlrPN%YqM~4CRSCt4c5#t%A8B>30_CPJ4a6%-rnF<1v2YG!Q zu9gL@tT0Q%vQc#<#?IxaP$x!X?)?S~0G|`zBFM5atNsQL9B&Ve&n*(uX}ytCADwdnwGfZ!$&ut^V^BByQdD_;!P~e+~4QK9fQ6I zZ0`{D#LKs=-#q%EABT2NJDNOl=B7^vOxXY6xmT@d+vY6arnp=F_3ZQ8G6r0+{Oqf4 zo4o1buNKT5`{>~7RtGw)zGwHZ1Kn@+TiAK|$>p(c*GxLl=IB*74E$n8{s)J{o_x6d zd4rE1{^gdm&sRVGY)=324~)+DT>e{!Ey#C#JFK0Kq z;=5JbFW{G7K+4lNw~!m`VDth#4&x@Y?01%J8w;*X!0y=~&IyU%*NSMsJ& zPiFtEMckG9Cv2O%b!gG99r3$I5C6DtLE5s-H(WUTgATXck@Q*rv3q_>oczInIa{ya zvZL;?m$P@?G2z8!4`m)69$qov#<8Ve7TmR^=ND79uioOnZ*R(bPdxq0`zIkvsG#$g z+?04Z1pF&bZemBaD);v^UB}^(#aw@Y9@1>*xTY_A<8u!!y=&3x{R0Z0%NSnp=v!N# z`?ttn-!|{{4ZH9B_=Ce=Tt48PPZRd78xwPW=K83Zzxv8*cG|ak)q_tQ^uGT@_kA6|%RD&inY4L>uO2z=AA3%8 z{e1Y*?wy;u2Rbu<^<@?3J&g#u4jG8iU}9T|(2Hz;d;Ysue>@`L`PRSYcn-F&Tl0J2 zRRcbJ^!D<{)~)>T>>U@rpE7-I`_gNQwCh=#UJ0; zf7S2NOZ%)joOJPlgPCXClRp2vdv=}pMVd;Hqc3)gRG`F--Pf=yHJYt?C3!3S4O3a_Xf+qr1v z?RQllIi7X$LCoUG&6(JKY<0dCt8puxshBw-;|dI`_Po8&fX0WYN)s^Y+)y|KtAT^&da~d(z^F z(I+oX{`u0J3>6^$|D~68#{O*|Q9S@yz z`Qdq0Z>-!kxcjyv7oWT+z3Ta)dHufJm%jOyhtDg!{|V2*!m~Sw7Nc(PKGG?%&slk$ zwtOxo1#XF-d*|5vjBQDOO}gUH)RW%4TN|iS3YH z)cJzruYb00pZB5JzyJK}t9`!y>FUj!ufOBYA-^8}*AHLp8JV;B`DQNh^0ZHv z{cz%`JI`D;__yQx);{#@;;mmSt2^@8r49cUNqOHJ+-k&m3}L#Uq~g|Gsff@7J%`vF`k4`NNt;CtOx}_0c;#F`xYuv9bS* zsC6$sw|05$hs&pZeWKac%d>BdF1YoV3)&>rhP_sDMf&?<#@edV<|*%daqrR}i?)7I z(XqO0-Jsk@ZusbvOCsMKU6yrud~)B)$qCVYU+tB6-L}`R%YO2K<5?X?%!qph#`F?5I(BvI2rY0SEI;HjMBVGT#^T@TmzV5#+xyb|5#{AkZzV*c) z#r-3-_YGHhNA|z-w<~+K-|rupcjd-gV+MII+)|kH`N)YI-*{%n%9u-QcMckMM}Er0 zZT$`|^7P-m^vU=Gku&#oeX!}US+{n%X~wKqcW(Lg#mmOWpW7|$(aJ4LhJD^=)6#K! zPoCI#$HGlDlPbHsa?$)_51#!?{{zb=);?E~^64uJ-Yx6@@QGhf&U)cs-S$f>pS$_f z=jT1NvTffP6|MFheDf2YIY;*Ra! z9F){Kd%>RYuE|||pSMhXDeubShhOda-0mGW&fU8Cz^%Pv2QBMY_EV1^s#DrOeBrOJ zT#)tF<~>u-8rb6gAJ*J?{mXm)GHTZozkbs-bJ0T|j^DTE$2H3zU)r+8M@5~lx^LC6 zrDtSZe$O3mcPvS4G0$JQYWSX)Hg-JNbIKDhy0;uYf5pkQVb5*7>FC9C+#|l9xGE#% zt+O`WJF9qF`ajEyFPffxZPVp%zw)U2+*>O~Z=HT~muWZO^wAl;%U8em=G$p^rQUr} zo48$V=I-k{^4HCh4y8-@3haZBDxpeYUjEI!1y!)=R_jyy@ zU1HNGz4f=Z-rbX1xT)h`r$6Jq?E81t%)jh`ZS%kM9w}b)L(yd;hfa8|e`=@O@3`!x z%@_E#-?y`8>`gb7#U4pn`9;gGSHH7%x@Yvxhi>+Z*H^wAy&=%{?OrPe-1f}s2fwL& zu$TYQ`(Kzmwcm}`bviS8()*LDdrder`pv2{zsbCQ#lo8&Omg41DsBGCE2iw9^V5d5 z2m5~4w(AuS&OA}pDd~)HeWwlHGqKhFe#g2M?|ir{Z2lwL&RhGzc|G!*-S+oIKTm!3 zgY>7K+57AhONV}v?)_}Z@0l}Cw2R5R`^npOZs?p;`R2oWzb)JQZTq((?#MfK*0`Zl zPJXoe*aZiEO!3_w8`*R3FE3VXpXV!i^uV=DSS-3dvmAj+ATk-YO1%Dg; z^S@VnKa7vrdq>*eVxAs;{OWJtU*2i%9jkBpZNryOZQt;D)T+rZf7fc;ebc)2{e9sR z@BRDayl1|?dCbA_hdcI7Y(B!DbMo8WSDctseZ$gACVi3IVa~?G-JcrMDY4b9zt&Ek z`pUkjTLS*yqS{=s{2ym+Ip^26=RG#1d*AzyUHanDg>6?APdu9N>=PS4IkqV5g*2w0j@le$jKg{`pbdW97{rIO}iMXC}ta zdUgH@%(opn>X==t?|c#^$>_$r*!ns(I+4Afsk_!jE908Jl%25h#AS;-m6dy*jlZGu z!7JZ;aBtYkFZ;#x&qw-u7bo z)8Af~J2`RuRr?1OJv`^K>naBvYc=UB#Xw#(?X>)-q9)L)iw9eDTk z=YE-b_uup0c<#MXCu){%_+g#vl2_BV7sOw@PbA$m@wU3hP7Z8))!d#P5@-G~DX(h( zs)&q@ukHM-)4Kb{hQB%e*=Jw*G$^{u-2;2H>l?nJn(1&D%uXC*MPMJ#8Lc@YH;wM> z4ejG2qhS&Tjv)gMlltwL?1L2p)B8@CA!|ntIj+ZcTFmuy@OH3RSfiV*wFGh%& zkbXOM{|K=TPZHOe<`5_l*8p~n3C4P?tlsAqZ+V|vv^#;t^5BU1E38ojFw>90^I$$d zcjM`3oc!DzSALb>BYwa4huz}}Cg#kR@NN>`U0yGfkmYjrqHw0;skIQ9t`#VG5i=Vh zTH(B0)?O*Eol(139$)3~1yTCv41Av6ck7O#-bZq}nm*qppM~=MTl@AAWiNu?pyWSuxZUzY92DR|ND2+hHhX;nrjzkjFh z2+fhYBo;z*r2ap^lkTY?|2TFH*#Cy$n+nXN#A6&+!`H92*x8M@TE&SlBBh`T{jD%^ zdGuUQYcbbU87|IE!(mvJWOe8G@Dh(a7U25(RLtmWuo|!&zH%+%W$k}+tc(+1!F)6D zSA`u9+^AEHO<&wfQH?E1yk=tq*bFfSdma{G?*sRc@a%;xEhClCykcX*0r+;@r^~Uo zqlvgJy051Qs&%=}b2A3dbBE!Yhc5^G&klY=-G0y)>47cyfoI4OzHCbVuyG#u6;XJa zB7Vln*!u?OlquQ6S^ZC>W0i&v$iG*_yZ)Qx?uBAdvoDu}?p2$$OxC3xz!4K?-k+!oDt^n$Snsr#_v zH3v&n#mUBhVsa4!{;5=jy(m4AG+aQ;Zb7)+ZdY&YyV3tru-&Ms_(xb;xJ!gFxo8J5 zVSx~bY=-~K!5!MOKpyNBnkat2lUwFNy*JSR;|-zzVXsi`HTMcG@o}7S?*_Fr(|s5$ zvL@Ro2gf132r~)yBwXO@GZDU@aVAEgfJ94zN}ZutWzK z-sBjAiu1OD}+C%gANzf%2LluY%X*BXXNa92`mV`;-bJ<7Eg={Voi5^kQP< z9z?XfBhVcBX+L*jY{0ra&mFq4v}Fb5S1# z#8p$Ru7+@%8lu2}rUtSNtpI@`RivRMRcw_iQg0nC84XvAO3NBd#6n*Kz?ch|&zU}K z^;TCd^feX3vW^atOVvl!M%A_#L&JNEFbv7@4OUZAI&1`V5{EgclA4nxNV?#nTwI@C z!KBmXA!j%#!^5ClwI#LR)x1`XKQfNGKl%|d}3%z_S#1&$Iybjld zjW`k@Ae&VH$U)YQm+pbZQvZy~$Zf_4~l z0Y5~bIzQ;i#G?dKX+cX42~wa;prk;ZTfBMb)3a{@((@ruBE6NkJj4aY@PXb++clA5 z==CW^6T!gb7@uNr$1()SmR8fm(EAQ021ldF3Org(0Y!+V#6Uu?mFYK3$n?tzLX+Ai zTKesWX7t{bMe`%prVKzsATs2S(&fhodWY;OWZq^&+S_P|UWN_BK)P9NGdA>ynA#Zi zA+|PFQgpc)EzGnGYEGWxMDH&@yy!2r-9Q_Dpn{rR6V&7ZL|2nFsGCZK=`E?Srh*Fd z0yn!n`T-YWT8qEbvde=)>x?c>69meSM8>DGDN4VD}j8wdjI+ zGNt2?WSkgx+utuddYp=j7qP#|4v&swRvf<*Mu_e>O|?kK$Ylr`y%04LEvON7WB@5N zq%=ZBNHeMr)-=b#Mzquj`!;GLT2LFQK*ZiAT52PF+nU|}Ppg5o8GIZmh+uZv(IQrO zFl@y=Ey#2yiNRxp^cg}ZJS9;0s4+7>nF*p%P2p=MhXA;5EIz8zz}FfLC4Wue{cXTk zNWUOdiaY})hQbmc8We~ZkEFN7b6&|hKallA)e9P4M4fTLj@nL;I%_6%))DvHMGKUv zflyv9YYfPRDxgVd4lRCtc<)DB4g_+!m=V;ZDyCwL;?^ofQZcHSavH@@IJ*KCQ_g1i zQ|$06Q)ICJi;;3(`N2=o3ori#P71l{7%A|0DJMTj6NRKmu1&@Xy(OI^Mhc3J8B)*~ zC8+8VYG-MJLNK&?wrJOK|Gz0HJVbXHN)iK>%@#unV==+>fEd(o(uC@1ga=Uc7$v%S zz=OzO=iM8qXyVbgl>XNGolUhw>Z)s}dO@udRKr)pwoIw~NeZJC#g3I?cY@gJE)wOO zG*KRihr5g0ywf=~z6#=E1Ys7J8QN+%*@shHav!T1P7YjDdJY;cPI9mJP*kL8;L>2< z#{6aUisTs!bvV<(Gd&btHBRyH#kq8sc+GHJQZ!ozOVE^rU|J>)BPYf>?*BYCDO;S9 z&7qs?jg&{r?0q(7-Ul7F)Do%PGOCNSS|(n~rMIG7@lvkNkb<`0qPbbw%?$}uF{8xj zcbbc46fhoqQowjDnK#3n8TS;BN|W6YKgjNgAN1}>;ft~1i&6Mug7{)Eqr=-}h_B)9 zP2r2R;ENah?il!s4dRQ9=fZwG@p1lT?JlhSEY*R*r8u-*JQ;Oy-(0SP$oUcRDRQjf z-rfzx?CF{tBhUhr*P|=N5D))r~ZlhDp{8 z14E$9$O$d1hJhhaY%q!(svCxk^1V56;>p$hW_I`W2Pb&*jIL)@Otl%s&7mJnZK{}B zNJ^<%n7Wi&Xkpa?YNObl(hbGb0;0ocJqWkd!rSdn_KXUDs8-;s%AViSZ7Bq%a0z{4 zYV}zDJdV+v9@|1Hri-Q+C<5h1_CSb{I%AH!vokS^*3zmNzUjwYJW#df>x@)ut77n? zBSSPIr(z~wdbt1KWmHT{t77ev;RY-64JD8==JVsqNEykJot>@}C;`QWK@+S>;d_b9 z&V)DnD5;fFASICpy}qSYDUi}qoM2T7YTJrU-jajqFqP7(-K_ZL9p^(SNON^v8LIUu zh2tWt$M9u}1_~;N@8XKX$W-NSKJ`^$#;d>xkt$1&Dl^7G32F*OFjHFshO_)qBkun^ zcPo6>UK%qvDv|h9F>|4roYb*V%mKbp+{)lawYGXS(yp>lGYvTnXxJBCZZ;q+f?^|J z6RZYIK%u#~mDPZ#Z7VhzFcZ0z22=+Jg%_x#H$hAio?6T+_(9Hrw4~QcT1y`Xwvv|O z0y=xyR?1Qs7y%=$%2KVQrHqkWf>pDvY~zIlH9XQa+Y0_*?%{yVY!@inQk-a2JQR&$ zBjPcGGb^6&z%m!3BV^p^=>(cYv5^oU=1Ezsx|3EYG#8_rQONaTYdQf|g`CCxus@NC z_iuWpXNOkZp?Iw2s!mSpZMeWBXLeE0pFWx9Xf07%KyS!+Nt9L?q*)&cB z3W?GRMu!Bwn)R3{tza&egH+uLiP8#2F|b)%(+Wm$YqJ#w@kfYIydol7TLw)Makef+ z4(_lV3Xcm-5Ur)adUFcgS_+(|3EWx}*po{~PS6DAx17x#4MZ@rjCqr-*dj1yBbH)d zv$m$dMlXg;P1`DU4WcbkToy5I%md%Dqk#hO1#*W04EM9>3kAk9TpKB{-kbt+JvNTN zBqigljV5rnTngMy3Ooq?5s8lad;aEWt++A-2UA2jMKz6{!3I}n)D+bq%3rrLnv-af zBsv5nn8VQBp?v?wduKc@Is<)@ic^9+y3=smP9|L3+XhV>w5hSJLPNc;y*j7I{xRti zjWi3vj9D_+hpxmMddMG4(z_fAI0;Q&`+qnMD<6fC+|sMe`-v$|u%cu#_Pe z0U@8^u1m|6&JTC zHpIn(km5*hZ50NPJFW^(EY-L8*}jjPQA4f7vka15VxIGT&&W_ zVykt*0icGs?X2SB7R83R?X2SB7R83R?X2SB7R5%mA-^%r>Bi?9_jHCCLU=WvSzLqz zif=i-aOQ)>TTtvZ!h^<#c)`|mdu*;zt@(sInaG00M2;8YnUS}b$lKg@utQkbJt&55 zRk6|~ck1Y@5qXM%oUW6i>#~-W+SRH)lJDHp8<8Ylj%OyBB1!(feBztmKX)IB+geG2 zM6fkUcCeD{ppk5oOA_%KNxpy2Owwy5*+G(=GtP7PxBi=f6H=R{1c_j4l0>3V#c|>T zF-%}QA{6p+Q4r%A=<@$&CO+1eu9=+QY=}L07a=qwU;yNmR zz-At|`#ZtEnb_RJZ3JFSeB*78!8V*JHYs6YkONRZ`xse z6lRCvduSmr4iD`0a%?b0oL6)IzO~BTRm8#FgAUAy!wVeE5r>R{Vk3j3S=}G*9&Anb zM@OsDQ=D9%=_sOPahqJ`6TTbL9BHXmQXOri5P5o}ajl21d{l8uTxe^-)yax0RpR1w zo*TEBe1f>x8nrF0)2yvK*;<1$ofEc3ZA9AkGMz^z-Zn}*wq|mwMQ#pomefX6t0H5RxY=JxOX}7pSXB&R#*udW( zwUu1)l)|^lH;?fGV2u96G*Ecm-x@xwkS zHtdBVqZvQb7$tJrW}xr`p9Oz6EB>xF{E)|tpD8hjfBD#4e8vMDUWaS^1D^$dcPoBu zK|!&>KkQ`2&-4mxj!)7R|G;O#k72h$pjNN2@49Yswvc`?#xYl$)?6yv(MVJw`z(FFl@@1mD&QsjDg`T?u=!v0&CMgswtd~ zP5Wrd87>*_W20JVZQ563xLP*tt2G@Vn?h}%YApF+C#kMi(&=ZBwV&4X`y7__x0G=s z4UH!x+Wr!)VJ^QyOEAsVUtlR3=EA*N#rd;G6$5M%7905ZEP^j1j1zq>`&rxOfoBt( zL#ERqYw>WKALPP;oQkqOA8=iN^w?|vEa{Ug@MghqRUe5LQ~mTV%v7B72kTQf_tDm< z&o1f=5n-#3%m70A92L@`zTpOQt?YuFg?;I^qm`bHIwY7`C-;j8Fm*YmNnLj>Os>*ggQg9IQiUyQ}cV%(1u| zA{L7|@|=VJ{7c1Ds0?pZ)c<(bK|F+#SnP)306Z6%n%V}Cc@D6l4zPR&*f0m!a0l23 z2iQml*eD0s*$%ML4zMu}un7*ZiGW4hbTG*QHrW9-#Q`?e0d|f9>|6)fGzZvu4zTkb zU>7*RraQo9IKXB)z-BqXX4_!QD0ob72@@>*Gvv<28!#D}@`JuBf*ze~@#tLX(VWO| zFrTXuC1Qw?G%%O@y)yAV4TA!QoLCT7`oVya3^mh1-x(p(4P!>9 z)0KF2I_Ru0G6J2JB8?CE(jq`7jXQq0Y)xYMiWn-ASiTghhej;lOborPBF13UiKSuI z9wY`hzG|X`!lHg{S_0`HBa+TAO(>PPhDkbEs86AWndzWwR&*HPI-N`kp$mlx0Kfbv z-63Q%kWO#7#aP27y`CDq;bwY}T+w6lFzAht^teG^WofKd{{iPv&-CSPEpDzhw)u^a z^z{1F zL0%o~$Hu5Wlr?1^sy9(xau1ek(y3V{!mqBi4SfoijJTya5(CLGlE76bA9*>c*J0=p z>ZwZ-k`)U9t+S*Wn23j9%tW21;7*N*^c6^QtAFP1o)j^$-Wu__+}G6~PZJHl^FT^G zK(6zO^eNb|E1x&=LzCL3($9_O#Ps+{(mdRVOU@=q19jCnoAjr2u~QM+>hS#7gpM^Y zKzQ&$y3_H^9+{l@Au5C}pSMkxboG{`Jz3J`N(${ZS)+|x8A(tx(;gq3a)c&7cxthW zR2zCtmP0AlR`tn61`rL>X<5tw30;9 zaHpI^5m8rd%~KNeN`ig3K^p{(z4_;XV0+uM8u`$MU52rhf?ujNnvwiDl6;~@{u~GL zsgkf_A_L?Zyyt55`pPnooa}Z&U#N5)2PlWd7y{zwLelwEiP1_;P{q?E6LB(AOq1mC z1vQBlohj;Keb<5(vH=Rl0VP_H(b3ZRbhPK0(4HsJwg9R~Ecswlv_gO@5n#7Fx;oRw zcK8+}wZDddk->ko539K`5ddep^9hGqrnNT?NEv{PLP6ue1iIiZe{P&y(xH%5wxKZ3J%% zPeO5#h`>hhIEEkI+IL4#;! zYG@RD?M!H-EwP=UsT><{yfp-gjD|@jk|5GHKswusbe4uxxr5?Lp;p&iAZ4d9TOuVw zECT@HH{OS$xX0YX2_@Gf2;LUn3dNYu;sGw+zyDrcRIL$~{K~x1j&U(t+5<07lZ=9~ zAfqY3>$5UCTVqsD=TwwpS{ZEs80B2%ujrAp-355td5I{;xd7kvEf?G#K+du;msu>H z6~*F0u?)~@7{`^siLy{O5}wAn%p730kK=s~umT5Ip#!YQ0p@pr6+6J@I>1UCV5JVQ zc@D7o4zO|uScL&n&O8* zh9O0n5|LkWrB@FvQ9-v9YQh5mz0c_AD}ll z9svh;m5aYavry^3yeF|TUA%%KlW|mr`EWGTZk<8!gLrDPHb{5#g9nf*EuRGyc6Xc ztkH3^jLQxA!-p2sl$H^#gvI0kxYEKvWmRQywRiYpykT5<>Tyv#aL>E0T$C?=`tGg& zD&HG?7X?74kJB1Bt%1`TIIV%x8aS~R5e(lYe|7ZTPmGl32O#GYRKj;4wadL%-`)&A`>JpsTU?o=Kgo|J9T)zcNBi2#a~giq#8eY;T>6Cz$FH((D8_qH28_M zc-a4^fH>=NO+x$mQLKn*KL#kd=kIgn;g|75GgQTDgj`-AnrZlzEBTY4RT2Za<7x!C zR=<8XnZKW>3aNtA$7v0m*1%~EoYuf;4V>1%X$_p#z-bMf*1%~EoYuf;4g5c$0Zw8$ z%jJBT6Jcx;k#l7}oipKBoSgS_j^7+7pZRm1&*%Bvd(62$*8|$% zBZR*XDZH4IMZ;ZLID6pamn(YW z%*M%Ysqh;leQ@^0$qn+{5jp@Tce3!y^L$wkU&(Y9PWAOQZYNgz7xM6bD9(JG!*CAA zIRYoQv~bbkY@DNU{((0Uj>Z3RILG6hfO8^Fe(z~APL@x_|8sDji*p)Iu8Z({NBo`< z%lSS-{{L?}CS$iyC3cQ_A$bM<2gDL^+xR09ztN$~LqGC*com{nUZpD4CtN4q>)rlf zV6Tfh(1FuHJ%GJY1(J{XI=IpQ@sh(Jee%)Rh80|t!V!V3_%+yTRgPVgl>KaIp;+=n zNHunQRRU7~f!`^>CSVmmjio<^=omaWBH(qE*oH_R{L%ua#?Qe2K>jHttkM5ph!Alq literal 0 HcmV?d00001