From bb41c539289e3dce53fc23d801a56e8b3173db6d Mon Sep 17 00:00:00 2001 From: emmanueldufour Date: Wed, 17 Dec 2025 10:58:45 +0700 Subject: [PATCH 1/5] Update XDDFDataSourcesFactory.java removed some inline classes ( because they are hard to debug/understand and are anonymous) and replaced these 4 anon classes with a single real class thereby shrinking code --- .../chart/XDDFDataSourcesFactory.java | 1004 ++++++++--------- 1 file changed, 466 insertions(+), 538 deletions(-) diff --git a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java index 8e89c3620dc..d97a9c8c146 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java @@ -16,7 +16,6 @@ * limitations under the License. * ==================================================================== */ - package org.apache.poi.xddf.usermodel.chart; import org.apache.poi.ss.usermodel.CellType; @@ -37,541 +36,470 @@ */ @Beta public class XDDFDataSourcesFactory { - - private XDDFDataSourcesFactory() { - } - - public static XDDFCategoryDataSource fromDataSource(final CTAxDataSource categoryDS) { - if (categoryDS == null) { - return null; - } - if (categoryDS.getNumRef() != null && categoryDS.getNumRef().getNumCache() != null) { - return new XDDFCategoryDataSource() { - private final CTNumData category = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); - private final String formatCode = category.isSetFormatCode() ? category.getFormatCode() : null; - - @Override - public boolean isCellRange() { - return true; - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public String getDataRangeReference() { - return categoryDS.getNumRef().getF(); - } - - @Override - public int getPointCount() { - return (int) category.getPtCount().getVal(); - } - - @Override - public String getPointAt(int index) { - if (category.sizeOfPtArray() <= index) { - throw new IllegalArgumentException("Cannot access 0-based index " + index + - " in point-array with " + category.sizeOfPtArray() + " items"); - } - return category.getPtArray(index).getV(); - } - - @Override - public String getFormatCode() { return formatCode; } - }; - } else if (categoryDS.getStrRef() != null && categoryDS.getStrRef().getStrCache() != null) { - return new XDDFCategoryDataSource() { - private final CTStrData category = (CTStrData) categoryDS.getStrRef().getStrCache().copy(); - - @Override - public boolean isCellRange() { - return true; - } - - @Override - public String getDataRangeReference() { - return categoryDS.getStrRef().getF(); - } - - @Override - public int getPointCount() { - return (int) category.getPtCount().getVal(); - } - - @Override - public String getPointAt(int index) { - return category.getPtArray(index).getV(); - } - - @Override - public String getFormatCode() { return null; } - }; - } else if (categoryDS.getNumLit() != null) { - return new XDDFCategoryDataSource() { - private final CTNumData category = (CTNumData) categoryDS.getNumLit().copy(); - private final String formatCode = category.isSetFormatCode() ? category.getFormatCode() : null; - - @Override - public boolean isCellRange() { - return false; - } - - @Override - public boolean isLiteral() { - return true; - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public boolean isReference() { - return false; - } - - @Override - public String getDataRangeReference() { - return null; - } - - @Override - public int getPointCount() { - return (int) category.getPtCount().getVal(); - } - - @Override - public String getPointAt(int index) { - return category.getPtArray(index).getV(); - } - - @Override - public String getFormatCode() { return formatCode; } - }; - } else if (categoryDS.getStrLit() != null) { - return new XDDFCategoryDataSource() { - private final CTStrData category = (CTStrData) categoryDS.getStrLit().copy(); - - @Override - public boolean isCellRange() { - return false; - } - - @Override - public boolean isLiteral() { - return true; - } - - @Override - public boolean isReference() { - return false; - } - - @Override - public String getDataRangeReference() { - return null; - } - - @Override - public int getPointCount() { - return (int) category.getPtCount().getVal(); - } - - @Override - public String getPointAt(int index) { - return category.getPtArray(index).getV(); - } - - @Override - public String getFormatCode() { return null; } - }; - } else { - return null; // in some weird cases the element is empty - } - } - - public static XDDFNumericalDataSource fromDataSource(final CTNumDataSource valuesDS) { - if (valuesDS == null) { - return null; - } - if (valuesDS.getNumRef() != null && valuesDS.getNumRef().getNumCache() != null) { - return new XDDFNumericalDataSource() { - private final CTNumData values = (CTNumData) valuesDS.getNumRef().getNumCache().copy(); - private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; - - @Override - public String getFormatCode() { - return formatCode; - } - - @Override - public void setFormatCode(String formatCode) { - this.formatCode = formatCode; - } - - @Override - public boolean isCellRange() { - return true; - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public boolean isReference() { - return true; - } - - @Override - public int getPointCount() { - return (int) values.getPtCount().getVal(); - } - - @Override - public Double getPointAt(int index) { - return Double.valueOf(values.getPtArray(index).getV()); - } - - @Override - public String getDataRangeReference() { - return valuesDS.getNumRef().getF(); - } - - @Override - public int getColIndex() { - return 0; - } - }; - } else if (valuesDS.getNumLit() != null) { - return new XDDFNumericalDataSource() { - private final CTNumData values = (CTNumData) valuesDS.getNumLit().copy(); - private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; - - @Override - public String getFormatCode() { - return formatCode; - } - - @Override - public void setFormatCode(String formatCode) { - this.formatCode = formatCode; - } - - @Override - public boolean isCellRange() { - return false; - } - - @Override - public boolean isLiteral() { - return true; - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public boolean isReference() { - return false; - } - - @Override - public int getPointCount() { - return (int) values.getPtCount().getVal(); - } - - @Override - public Double getPointAt(int index) { - return Double.valueOf(values.getPtArray(index).getV()); - } - - @Override - public String getDataRangeReference() { - return null; - } - - @Override - public int getColIndex() { - return 0; - } - }; - } else { - return null; // in some weird cases the element is empty - } - } - - public static XDDFNumericalDataSource fromArray(T[] elements) { - return new LiteralNumericalArrayDataSource<>(elements); - } - - public static XDDFCategoryDataSource fromArray(String[] elements) { - return new LiteralStringArrayDataSource(elements); - } - - public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange) { - return new NumericalArrayDataSource<>(elements, dataRange); - } - - public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange) { - return new StringArrayDataSource(elements, dataRange); - } - - public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange, int col) { - return new NumericalArrayDataSource<>(elements, dataRange, col); - } - - public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange, int col) { - return new StringArrayDataSource(elements, dataRange, col); - } - - public static XDDFNumericalDataSource fromNumericCellRange(XSSFSheet sheet, - CellRangeAddress cellRangeAddress) { - return new NumericalCellRangeDataSource(sheet, cellRangeAddress); - } - - public static XDDFCategoryDataSource fromStringCellRange(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { - return new StringCellRangeDataSource(sheet, cellRangeAddress); - } - - private abstract static class AbstractArrayDataSource implements XDDFDataSource { - private final T[] elements; - private final String dataRange; - private int col = 0; - - public AbstractArrayDataSource(T[] elements, String dataRange) { - this.elements = elements.clone(); - this.dataRange = dataRange; - } - - public AbstractArrayDataSource(T[] elements, String dataRange, int col) { - this.elements = elements.clone(); - this.dataRange = dataRange; - this.col = col; - } - - @Override - public int getPointCount() { - return elements.length; - } - - @Override - public T getPointAt(int index) { - return elements[index]; - } - - @Override - public boolean isCellRange() { - return false; - } - - @Override - public boolean isReference() { - return dataRange != null; - } - - @Override - public boolean isNumeric() { - Class arrayComponentType = elements.getClass().getComponentType(); - return (Number.class.isAssignableFrom(arrayComponentType)); - } - - /** - * @return the dataRange - * @throws UnsupportedOperationException if there is no Data Range Reference - */ - @Override - public String getDataRangeReference() { - if (dataRange == null) { - throw new UnsupportedOperationException("Literal data source can not be expressed by reference."); - } else { - return dataRange; - } - } - - @Override - public int getColIndex() { - return col; - } - } - - private static class NumericalArrayDataSource extends AbstractArrayDataSource - implements XDDFNumericalDataSource { - private String formatCode; - - public NumericalArrayDataSource(T[] elements, String dataRange) { - super(elements, dataRange); - } - - public NumericalArrayDataSource(T[] elements, String dataRange, int col) { - super(elements, dataRange, col); - } - - @Override - public String getFormatCode() { - return formatCode; - } - - @Override - public void setFormatCode(String formatCode) { - this.formatCode = formatCode; - } - } - - private static class StringArrayDataSource extends AbstractArrayDataSource - implements XDDFCategoryDataSource { - public StringArrayDataSource(String[] elements, String dataRange) { - super(elements, dataRange); - } - - public StringArrayDataSource(String[] elements, String dataRange, int col) { - super(elements, dataRange, col); - } - - @Override - public String getFormatCode() { return null; } - } - - private static class LiteralNumericalArrayDataSource extends NumericalArrayDataSource { - public LiteralNumericalArrayDataSource(T[] elements) { - super(elements, null, 0); - } - - @Override - public boolean isLiteral() { - return true; - } - } - - private static class LiteralStringArrayDataSource extends StringArrayDataSource { - public LiteralStringArrayDataSource(String[] elements) { - super(elements, null, 0); - } - - @Override - public boolean isLiteral() { - return true; - } - } - - private abstract static class AbstractCellRangeDataSource implements XDDFDataSource { - private final XSSFSheet sheet; - private final CellRangeAddress cellRangeAddress; - private final int numOfCells; - private final XSSFFormulaEvaluator evaluator; - - protected AbstractCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { - this.sheet = sheet; - // Make copy since CellRangeAddress is mutable. - this.cellRangeAddress = cellRangeAddress.copy(); - this.numOfCells = this.cellRangeAddress.getNumberOfCells(); - this.evaluator = sheet.getWorkbook().getCreationHelper().createFormulaEvaluator(); - } - - @Override - public int getPointCount() { - return numOfCells; - } - - @Override - public boolean isCellRange() { - return true; - } - - @Override - public boolean isReference() { - return true; - } - - @Override - public int getColIndex() { - return cellRangeAddress.getFirstColumn(); - } - - @Override - public String getDataRangeReference() { - return cellRangeAddress.formatAsString(sheet.getSheetName(), true); - } - - protected CellValue getCellValueAt(int index) { - if (index < 0 || index >= numOfCells) { - throw new IndexOutOfBoundsException( - "Index must be between 0 and " + (numOfCells - 1) + " (inclusive), given: " + index); - } - int firstRow = cellRangeAddress.getFirstRow(); - int firstCol = cellRangeAddress.getFirstColumn(); - int lastCol = cellRangeAddress.getLastColumn(); - int width = lastCol - firstCol + 1; - int rowIndex = firstRow + index / width; - int cellIndex = firstCol + index % width; - XSSFRow row = sheet.getRow(rowIndex); - return (row == null) ? null : evaluator.evaluate(row.getCell(cellIndex)); - } - } - - private static class NumericalCellRangeDataSource extends AbstractCellRangeDataSource - implements XDDFNumericalDataSource { - protected NumericalCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { - super(sheet, cellRangeAddress); - } - - private String formatCode; - - @Override - public String getFormatCode() { - return formatCode; - } - - @Override - public void setFormatCode(String formatCode) { - this.formatCode = formatCode; - } - - @Override - public Double getPointAt(int index) { - CellValue cellValue = getCellValueAt(index); - if (cellValue != null && cellValue.getCellType() == CellType.NUMERIC) { - return cellValue.getNumberValue(); - } else { - return null; - } - } - - @Override - public boolean isNumeric() { - return true; - } - } - - private static class StringCellRangeDataSource extends AbstractCellRangeDataSource - implements XDDFCategoryDataSource { - protected StringCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { - super(sheet, cellRangeAddress); - } - - @Override - public String getPointAt(int index) { - CellValue cellValue = getCellValueAt(index); - if (cellValue != null && cellValue.getCellType() == CellType.STRING) { - return cellValue.getStringValue(); - } else { - return null; - } - } - - @Override - public boolean isNumeric() { - return false; - } - - @Override - public String getFormatCode() { return null; } - } + private XDDFDataSourcesFactory() { + } + + public static XDDFCategoryDataSource fromDataSource(final CTAxDataSource categoryDS) { + if (categoryDS == null) { + return null; + } + return new XDDFCategoryDataSourceImpl(categoryDS); + + } + + public static XDDFNumericalDataSource fromDataSource(final CTNumDataSource valuesDS) { + if (valuesDS == null) { + return null; + } + if (valuesDS.getNumRef() != null && valuesDS.getNumRef().getNumCache() != null) { + return new XDDFNumericalDataSource() { + private final CTNumData values = (CTNumData) valuesDS.getNumRef().getNumCache().copy(); + private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + + @Override + public boolean isCellRange() { + return true; + } + + @Override + public boolean isNumeric() { + return true; + } + + @Override + public boolean isReference() { + return true; + } + + @Override + public int getPointCount() { + return (int) values.getPtCount().getVal(); + } + + @Override + public Double getPointAt(int index) { + return Double.valueOf(values.getPtArray(index).getV()); + } + + @Override + public String getDataRangeReference() { + return valuesDS.getNumRef().getF(); + } + + @Override + public int getColIndex() { + return 0; + } + }; + } else if (valuesDS.getNumLit() != null) { + return new XDDFNumericalDataSource() { + private final CTNumData values = (CTNumData) valuesDS.getNumLit().copy(); + private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + + @Override + public boolean isCellRange() { + return false; + } + + @Override + public boolean isLiteral() { + return true; + } + + @Override + public boolean isNumeric() { + return true; + } + + @Override + public boolean isReference() { + return false; + } + + @Override + public int getPointCount() { + return (int) values.getPtCount().getVal(); + } + + @Override + public Double getPointAt(int index) { + return Double.valueOf(values.getPtArray(index).getV()); + } + + @Override + public String getDataRangeReference() { + return null; + } + + @Override + public int getColIndex() { + return 0; + } + }; + } else { + return null; // in some weird cases the element is empty + } + } + + public static XDDFNumericalDataSource fromArray(T[] elements) { + return new LiteralNumericalArrayDataSource<>(elements); + } + + public static XDDFCategoryDataSource fromArray(String[] elements) { + return new LiteralStringArrayDataSource(elements); + } + + public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange) { + return new NumericalArrayDataSource<>(elements, dataRange); + } + + public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange) { + return new StringArrayDataSource(elements, dataRange); + } + + public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange, int col) { + return new NumericalArrayDataSource<>(elements, dataRange, col); + } + + public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange, int col) { + return new StringArrayDataSource(elements, dataRange, col); + } + + public static XDDFNumericalDataSource fromNumericCellRange(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + return new NumericalCellRangeDataSource(sheet, cellRangeAddress); + } + + public static XDDFCategoryDataSource fromStringCellRange(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + return new StringCellRangeDataSource(sheet, cellRangeAddress); + } + + private abstract static class AbstractArrayDataSource implements XDDFDataSource { + private final T[] elements; + private final String dataRange; + private int col = 0; + + public AbstractArrayDataSource(T[] elements, String dataRange) { + this.elements = elements.clone(); + this.dataRange = dataRange; + } + + public AbstractArrayDataSource(T[] elements, String dataRange, int col) { + this.elements = elements.clone(); + this.dataRange = dataRange; + this.col = col; + } + + @Override + public int getPointCount() { + return elements.length; + } + + @Override + public T getPointAt(int index) { + return elements[index]; + } + + @Override + public boolean isCellRange() { + return false; + } + + @Override + public boolean isReference() { + return dataRange != null; + } + + @Override + public boolean isNumeric() { + Class arrayComponentType = elements.getClass().getComponentType(); + return (Number.class.isAssignableFrom(arrayComponentType)); + } + + /** + * @return the dataRange + * @throws UnsupportedOperationException if there is no Data Range Reference + */ + @Override + public String getDataRangeReference() { + if (dataRange == null) { + throw new UnsupportedOperationException("Literal data source can not be expressed by reference."); + } else { + return dataRange; + } + } + + @Override + public int getColIndex() { + return col; + } + } + + private static class NumericalArrayDataSource extends AbstractArrayDataSource implements XDDFNumericalDataSource { + private String formatCode; + + public NumericalArrayDataSource(T[] elements, String dataRange) { + super(elements, dataRange); + } + + public NumericalArrayDataSource(T[] elements, String dataRange, int col) { + super(elements, dataRange, col); + } + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + } + + private static class StringArrayDataSource extends AbstractArrayDataSource implements XDDFCategoryDataSource { + public StringArrayDataSource(String[] elements, String dataRange) { + super(elements, dataRange); + } + + public StringArrayDataSource(String[] elements, String dataRange, int col) { + super(elements, dataRange, col); + } + + @Override + public String getFormatCode() { + return null; + } + } + + private static class LiteralNumericalArrayDataSource extends NumericalArrayDataSource { + public LiteralNumericalArrayDataSource(T[] elements) { + super(elements, null, 0); + } + + @Override + public boolean isLiteral() { + return true; + } + } + + private static class LiteralStringArrayDataSource extends StringArrayDataSource { + public LiteralStringArrayDataSource(String[] elements) { + super(elements, null, 0); + } + + @Override + public boolean isLiteral() { + return true; + } + } + + private abstract static class AbstractCellRangeDataSource implements XDDFDataSource { + private final XSSFSheet sheet; + private final CellRangeAddress cellRangeAddress; + private final int numOfCells; + private final XSSFFormulaEvaluator evaluator; + + protected AbstractCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + this.sheet = sheet; + // Make copy since CellRangeAddress is mutable. + this.cellRangeAddress = cellRangeAddress.copy(); + this.numOfCells = this.cellRangeAddress.getNumberOfCells(); + this.evaluator = sheet.getWorkbook().getCreationHelper().createFormulaEvaluator(); + } + + @Override + public int getPointCount() { + return numOfCells; + } + + @Override + public boolean isCellRange() { + return true; + } + + @Override + public boolean isReference() { + return true; + } + + @Override + public int getColIndex() { + return cellRangeAddress.getFirstColumn(); + } + + @Override + public String getDataRangeReference() { + return cellRangeAddress.formatAsString(sheet.getSheetName(), true); + } + + protected CellValue getCellValueAt(int index) { + if (index < 0 || index >= numOfCells) { + throw new IndexOutOfBoundsException("Index must be between 0 and " + (numOfCells - 1) + " (inclusive), given: " + index); + } + int firstRow = cellRangeAddress.getFirstRow(); + int firstCol = cellRangeAddress.getFirstColumn(); + int lastCol = cellRangeAddress.getLastColumn(); + int width = lastCol - firstCol + 1; + int rowIndex = firstRow + index / width; + int cellIndex = firstCol + index % width; + XSSFRow row = sheet.getRow(rowIndex); + return (row == null) ? null : evaluator.evaluate(row.getCell(cellIndex)); + } + } + + private static class NumericalCellRangeDataSource extends AbstractCellRangeDataSource implements XDDFNumericalDataSource { + protected NumericalCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + super(sheet, cellRangeAddress); + } + + private String formatCode; + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + + @Override + public Double getPointAt(int index) { + CellValue cellValue = getCellValueAt(index); + if (cellValue != null && cellValue.getCellType() == CellType.NUMERIC) { + return cellValue.getNumberValue(); + } else { + return null; + } + } + + @Override + public boolean isNumeric() { + return true; + } + } + + private static class StringCellRangeDataSource extends AbstractCellRangeDataSource implements XDDFCategoryDataSource { + protected StringCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + super(sheet, cellRangeAddress); + } + + @Override + public String getPointAt(int index) { + CellValue cellValue = getCellValueAt(index); + if (cellValue != null && cellValue.getCellType() == CellType.STRING) { + return cellValue.getStringValue(); + } else { + return null; + } + } + + @Override + public boolean isNumeric() { + return false; + } + + @Override + public String getFormatCode() { + return null; + } + } + + public static class XDDFCategoryDataSourceImpl implements XDDFCategoryDataSource { + CTAxDataSource categoryDS; + private final CTStrData categoryString; + private final CTNumData categoryNumber; + private final String formatCode; + + enum Type { + FROM_NUMBER_REFERENCE, FROM_STRING_REFERENCE, FROM_STRING_LITERAL, FROM_NUMBER_LITERAL, ERROR + } + + public final Type type; + + public XDDFCategoryDataSourceImpl(CTAxDataSource categoryDS) { + if (categoryDS.getNumRef() != null && categoryDS.getNumRef().getNumCache() != null) { + type = Type.FROM_NUMBER_REFERENCE; + categoryNumber = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); + categoryString = null; + } else if (categoryDS.getStrRef() != null && categoryDS.getStrRef().getStrCache() != null) { + type = Type.FROM_STRING_REFERENCE; + categoryString = (CTStrData) categoryDS.getStrRef().getStrCache().copy(); + categoryNumber = null; + } else if (categoryDS.getNumLit() != null) { + type = Type.FROM_NUMBER_LITERAL; + categoryNumber = (CTNumData) categoryDS.getNumLit().copy(); + categoryString = null; + } else if (categoryDS.getStrLit() != null) { + type = Type.FROM_STRING_LITERAL; + this.categoryDS = categoryDS; + categoryString = (CTStrData) categoryDS.getStrLit().copy(); + categoryNumber = null; + } else { + this.categoryDS = categoryDS; + categoryNumber = null; + categoryString = null; + type = Type.ERROR; // in some weird cases the element is empty + } + if (type == Type.FROM_NUMBER_LITERAL || type == Type.FROM_NUMBER_REFERENCE) + formatCode = categoryNumber.isSetFormatCode() ? categoryNumber.getFormatCode() : null; + else + formatCode = null; + } + + @Override + public int getPointCount() { + if (categoryString != null) { + return (int) categoryString.getPtCount().getVal(); + } + return (int) categoryNumber.getPtCount().getVal(); + } + + @Override + public String getPointAt(int index) { + if (categoryString != null) + return categoryString.getPtArray(index).getV(); + return categoryNumber.getPtArray(index).getV(); + } + + @Override + public boolean isCellRange() { + return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_STRING_REFERENCE); + } + + public String getDataRangeReference() { + switch (type) { + case FROM_NUMBER_REFERENCE: + return categoryDS.getNumRef().getF(); + case FROM_STRING_REFERENCE: + return categoryDS.getStrRef().getF(); + } + return null; + } + + public String getFormatCode() { + return formatCode; + } + } } From cc49d6eab278849a1683604b7fa916c8e5e0e2c8 Mon Sep 17 00:00:00 2001 From: emmanueldufour Date: Wed, 17 Dec 2025 14:07:59 +0700 Subject: [PATCH 2/5] Update XDDFDataSourcesFactory.java fix bug --- .../apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java index d97a9c8c146..150d939c77f 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java @@ -439,6 +439,7 @@ enum Type { public final Type type; public XDDFCategoryDataSourceImpl(CTAxDataSource categoryDS) { + this.categoryDS=categoryDS; if (categoryDS.getNumRef() != null && categoryDS.getNumRef().getNumCache() != null) { type = Type.FROM_NUMBER_REFERENCE; categoryNumber = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); @@ -457,7 +458,6 @@ public XDDFCategoryDataSourceImpl(CTAxDataSource categoryDS) { categoryString = (CTStrData) categoryDS.getStrLit().copy(); categoryNumber = null; } else { - this.categoryDS = categoryDS; categoryNumber = null; categoryString = null; type = Type.ERROR; // in some weird cases the element is empty From d49524f768665a4640c92e250c8c75d28e3ffcf1 Mon Sep 17 00:00:00 2001 From: emmanueldufour Date: Thu, 18 Dec 2025 08:54:44 +0700 Subject: [PATCH 3/5] Update XDDFDataSourcesFactory.java added whitespaces as required --- .../apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java index 150d939c77f..772aa897ab1 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java @@ -439,7 +439,7 @@ enum Type { public final Type type; public XDDFCategoryDataSourceImpl(CTAxDataSource categoryDS) { - this.categoryDS=categoryDS; + this.categoryDS = categoryDS; if (categoryDS.getNumRef() != null && categoryDS.getNumRef().getNumCache() != null) { type = Type.FROM_NUMBER_REFERENCE; categoryNumber = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); From 61049a092c0db47577f228f224d4c144bc4611c8 Mon Sep 17 00:00:00 2001 From: emmanueldufour Date: Fri, 19 Dec 2025 15:06:48 +0700 Subject: [PATCH 4/5] Update XDDFDataSourcesFactory.java forgot a couple function --- .../usermodel/chart/XDDFDataSourcesFactory.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java index 772aa897ab1..5420d353f92 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java @@ -439,7 +439,7 @@ enum Type { public final Type type; public XDDFCategoryDataSourceImpl(CTAxDataSource categoryDS) { - this.categoryDS = categoryDS; + this.categoryDS=categoryDS; if (categoryDS.getNumRef() != null && categoryDS.getNumRef().getNumCache() != null) { type = Type.FROM_NUMBER_REFERENCE; categoryNumber = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); @@ -488,6 +488,10 @@ public boolean isCellRange() { return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_STRING_REFERENCE); } + public boolean isReference() { + return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_STRING_REFERENCE); + } + public String getDataRangeReference() { switch (type) { case FROM_NUMBER_REFERENCE: @@ -501,5 +505,14 @@ public String getDataRangeReference() { public String getFormatCode() { return formatCode; } + + public boolean isNumeric() { + return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_NUMBER_LITERAL); + } + + public boolean isLiteral() { + return (type == Type.FROM_STRING_LITERAL || type == Type.FROM_NUMBER_LITERAL); + } + } } From 2814e2fcdadb650dbbc251300071cc0f2c715a74 Mon Sep 17 00:00:00 2001 From: emmanueldufour Date: Fri, 19 Dec 2025 16:50:11 +0700 Subject: [PATCH 5/5] Update XDDFDataSourcesFactory.java spacing --- .../chart/XDDFDataSourcesFactory.java | 960 +++++++++--------- 1 file changed, 483 insertions(+), 477 deletions(-) diff --git a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java index 5420d353f92..cc181bfa987 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xddf/usermodel/chart/XDDFDataSourcesFactory.java @@ -36,483 +36,489 @@ */ @Beta public class XDDFDataSourcesFactory { - private XDDFDataSourcesFactory() { - } - - public static XDDFCategoryDataSource fromDataSource(final CTAxDataSource categoryDS) { - if (categoryDS == null) { - return null; - } - return new XDDFCategoryDataSourceImpl(categoryDS); - - } - - public static XDDFNumericalDataSource fromDataSource(final CTNumDataSource valuesDS) { - if (valuesDS == null) { - return null; - } - if (valuesDS.getNumRef() != null && valuesDS.getNumRef().getNumCache() != null) { - return new XDDFNumericalDataSource() { - private final CTNumData values = (CTNumData) valuesDS.getNumRef().getNumCache().copy(); - private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; - - @Override - public String getFormatCode() { - return formatCode; - } - - @Override - public void setFormatCode(String formatCode) { - this.formatCode = formatCode; - } - - @Override - public boolean isCellRange() { - return true; - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public boolean isReference() { - return true; - } - - @Override - public int getPointCount() { - return (int) values.getPtCount().getVal(); - } - - @Override - public Double getPointAt(int index) { - return Double.valueOf(values.getPtArray(index).getV()); - } - - @Override - public String getDataRangeReference() { - return valuesDS.getNumRef().getF(); - } - - @Override - public int getColIndex() { - return 0; - } - }; - } else if (valuesDS.getNumLit() != null) { - return new XDDFNumericalDataSource() { - private final CTNumData values = (CTNumData) valuesDS.getNumLit().copy(); - private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; - - @Override - public String getFormatCode() { - return formatCode; - } - - @Override - public void setFormatCode(String formatCode) { - this.formatCode = formatCode; - } - - @Override - public boolean isCellRange() { - return false; - } - - @Override - public boolean isLiteral() { - return true; - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public boolean isReference() { - return false; - } - - @Override - public int getPointCount() { - return (int) values.getPtCount().getVal(); - } - - @Override - public Double getPointAt(int index) { - return Double.valueOf(values.getPtArray(index).getV()); - } - - @Override - public String getDataRangeReference() { - return null; - } - - @Override - public int getColIndex() { - return 0; - } - }; - } else { - return null; // in some weird cases the element is empty - } - } - - public static XDDFNumericalDataSource fromArray(T[] elements) { - return new LiteralNumericalArrayDataSource<>(elements); - } - - public static XDDFCategoryDataSource fromArray(String[] elements) { - return new LiteralStringArrayDataSource(elements); - } - - public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange) { - return new NumericalArrayDataSource<>(elements, dataRange); - } - - public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange) { - return new StringArrayDataSource(elements, dataRange); - } - - public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange, int col) { - return new NumericalArrayDataSource<>(elements, dataRange, col); - } - - public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange, int col) { - return new StringArrayDataSource(elements, dataRange, col); - } - - public static XDDFNumericalDataSource fromNumericCellRange(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { - return new NumericalCellRangeDataSource(sheet, cellRangeAddress); - } - - public static XDDFCategoryDataSource fromStringCellRange(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { - return new StringCellRangeDataSource(sheet, cellRangeAddress); - } - - private abstract static class AbstractArrayDataSource implements XDDFDataSource { - private final T[] elements; - private final String dataRange; - private int col = 0; - - public AbstractArrayDataSource(T[] elements, String dataRange) { - this.elements = elements.clone(); - this.dataRange = dataRange; - } - - public AbstractArrayDataSource(T[] elements, String dataRange, int col) { - this.elements = elements.clone(); - this.dataRange = dataRange; - this.col = col; - } - - @Override - public int getPointCount() { - return elements.length; - } - - @Override - public T getPointAt(int index) { - return elements[index]; - } - - @Override - public boolean isCellRange() { - return false; - } - - @Override - public boolean isReference() { - return dataRange != null; - } - - @Override - public boolean isNumeric() { - Class arrayComponentType = elements.getClass().getComponentType(); - return (Number.class.isAssignableFrom(arrayComponentType)); - } - - /** - * @return the dataRange - * @throws UnsupportedOperationException if there is no Data Range Reference - */ - @Override - public String getDataRangeReference() { - if (dataRange == null) { - throw new UnsupportedOperationException("Literal data source can not be expressed by reference."); - } else { - return dataRange; - } - } - - @Override - public int getColIndex() { - return col; - } - } - - private static class NumericalArrayDataSource extends AbstractArrayDataSource implements XDDFNumericalDataSource { - private String formatCode; - - public NumericalArrayDataSource(T[] elements, String dataRange) { - super(elements, dataRange); - } - - public NumericalArrayDataSource(T[] elements, String dataRange, int col) { - super(elements, dataRange, col); - } - - @Override - public String getFormatCode() { - return formatCode; - } - - @Override - public void setFormatCode(String formatCode) { - this.formatCode = formatCode; - } - } - - private static class StringArrayDataSource extends AbstractArrayDataSource implements XDDFCategoryDataSource { - public StringArrayDataSource(String[] elements, String dataRange) { - super(elements, dataRange); - } - - public StringArrayDataSource(String[] elements, String dataRange, int col) { - super(elements, dataRange, col); - } - - @Override - public String getFormatCode() { - return null; - } - } - - private static class LiteralNumericalArrayDataSource extends NumericalArrayDataSource { - public LiteralNumericalArrayDataSource(T[] elements) { - super(elements, null, 0); - } - - @Override - public boolean isLiteral() { - return true; - } - } - - private static class LiteralStringArrayDataSource extends StringArrayDataSource { - public LiteralStringArrayDataSource(String[] elements) { - super(elements, null, 0); - } - - @Override - public boolean isLiteral() { - return true; - } - } - - private abstract static class AbstractCellRangeDataSource implements XDDFDataSource { - private final XSSFSheet sheet; - private final CellRangeAddress cellRangeAddress; - private final int numOfCells; - private final XSSFFormulaEvaluator evaluator; - - protected AbstractCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { - this.sheet = sheet; - // Make copy since CellRangeAddress is mutable. - this.cellRangeAddress = cellRangeAddress.copy(); - this.numOfCells = this.cellRangeAddress.getNumberOfCells(); - this.evaluator = sheet.getWorkbook().getCreationHelper().createFormulaEvaluator(); - } - - @Override - public int getPointCount() { - return numOfCells; - } - - @Override - public boolean isCellRange() { - return true; - } - - @Override - public boolean isReference() { - return true; - } - - @Override - public int getColIndex() { - return cellRangeAddress.getFirstColumn(); - } - - @Override - public String getDataRangeReference() { - return cellRangeAddress.formatAsString(sheet.getSheetName(), true); - } - - protected CellValue getCellValueAt(int index) { - if (index < 0 || index >= numOfCells) { - throw new IndexOutOfBoundsException("Index must be between 0 and " + (numOfCells - 1) + " (inclusive), given: " + index); - } - int firstRow = cellRangeAddress.getFirstRow(); - int firstCol = cellRangeAddress.getFirstColumn(); - int lastCol = cellRangeAddress.getLastColumn(); - int width = lastCol - firstCol + 1; - int rowIndex = firstRow + index / width; - int cellIndex = firstCol + index % width; - XSSFRow row = sheet.getRow(rowIndex); - return (row == null) ? null : evaluator.evaluate(row.getCell(cellIndex)); - } - } - - private static class NumericalCellRangeDataSource extends AbstractCellRangeDataSource implements XDDFNumericalDataSource { - protected NumericalCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { - super(sheet, cellRangeAddress); - } - - private String formatCode; - - @Override - public String getFormatCode() { - return formatCode; - } - - @Override - public void setFormatCode(String formatCode) { - this.formatCode = formatCode; - } - - @Override - public Double getPointAt(int index) { - CellValue cellValue = getCellValueAt(index); - if (cellValue != null && cellValue.getCellType() == CellType.NUMERIC) { - return cellValue.getNumberValue(); - } else { - return null; - } - } - - @Override - public boolean isNumeric() { - return true; - } - } - - private static class StringCellRangeDataSource extends AbstractCellRangeDataSource implements XDDFCategoryDataSource { - protected StringCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { - super(sheet, cellRangeAddress); - } - - @Override - public String getPointAt(int index) { - CellValue cellValue = getCellValueAt(index); - if (cellValue != null && cellValue.getCellType() == CellType.STRING) { - return cellValue.getStringValue(); - } else { - return null; - } - } - - @Override - public boolean isNumeric() { - return false; - } - - @Override - public String getFormatCode() { - return null; - } - } - - public static class XDDFCategoryDataSourceImpl implements XDDFCategoryDataSource { - CTAxDataSource categoryDS; - private final CTStrData categoryString; - private final CTNumData categoryNumber; - private final String formatCode; - - enum Type { - FROM_NUMBER_REFERENCE, FROM_STRING_REFERENCE, FROM_STRING_LITERAL, FROM_NUMBER_LITERAL, ERROR - } - - public final Type type; - - public XDDFCategoryDataSourceImpl(CTAxDataSource categoryDS) { - this.categoryDS=categoryDS; - if (categoryDS.getNumRef() != null && categoryDS.getNumRef().getNumCache() != null) { - type = Type.FROM_NUMBER_REFERENCE; - categoryNumber = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); - categoryString = null; - } else if (categoryDS.getStrRef() != null && categoryDS.getStrRef().getStrCache() != null) { - type = Type.FROM_STRING_REFERENCE; - categoryString = (CTStrData) categoryDS.getStrRef().getStrCache().copy(); - categoryNumber = null; - } else if (categoryDS.getNumLit() != null) { - type = Type.FROM_NUMBER_LITERAL; - categoryNumber = (CTNumData) categoryDS.getNumLit().copy(); - categoryString = null; - } else if (categoryDS.getStrLit() != null) { - type = Type.FROM_STRING_LITERAL; - this.categoryDS = categoryDS; - categoryString = (CTStrData) categoryDS.getStrLit().copy(); - categoryNumber = null; - } else { - categoryNumber = null; - categoryString = null; - type = Type.ERROR; // in some weird cases the element is empty - } - if (type == Type.FROM_NUMBER_LITERAL || type == Type.FROM_NUMBER_REFERENCE) - formatCode = categoryNumber.isSetFormatCode() ? categoryNumber.getFormatCode() : null; - else - formatCode = null; - } - - @Override - public int getPointCount() { - if (categoryString != null) { - return (int) categoryString.getPtCount().getVal(); - } - return (int) categoryNumber.getPtCount().getVal(); - } - - @Override - public String getPointAt(int index) { - if (categoryString != null) - return categoryString.getPtArray(index).getV(); - return categoryNumber.getPtArray(index).getV(); - } - - @Override - public boolean isCellRange() { - return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_STRING_REFERENCE); - } - - public boolean isReference() { - return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_STRING_REFERENCE); - } - - public String getDataRangeReference() { - switch (type) { - case FROM_NUMBER_REFERENCE: - return categoryDS.getNumRef().getF(); - case FROM_STRING_REFERENCE: - return categoryDS.getStrRef().getF(); - } - return null; - } - - public String getFormatCode() { - return formatCode; - } - - public boolean isNumeric() { - return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_NUMBER_LITERAL); - } - - public boolean isLiteral() { + private XDDFDataSourcesFactory() { + } + + public static XDDFCategoryDataSource fromDataSource(final CTAxDataSource categoryDS) { + if (categoryDS == null) { + return null; + } + return new XDDFCategoryDataSourceImpl(categoryDS); + + } + + public static XDDFNumericalDataSource fromDataSource(final CTNumDataSource valuesDS) { + if (valuesDS == null) { + return null; + } + if (valuesDS.getNumRef() != null && valuesDS.getNumRef().getNumCache() != null) { + return new XDDFNumericalDataSource() { + private final CTNumData values = (CTNumData) valuesDS.getNumRef().getNumCache().copy(); + private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + + @Override + public boolean isCellRange() { + return true; + } + + @Override + public boolean isNumeric() { + return true; + } + + @Override + public boolean isReference() { + return true; + } + + @Override + public int getPointCount() { + return (int) values.getPtCount().getVal(); + } + + @Override + public Double getPointAt(int index) { + return Double.valueOf(values.getPtArray(index).getV()); + } + + @Override + public String getDataRangeReference() { + return valuesDS.getNumRef().getF(); + } + + @Override + public int getColIndex() { + return 0; + } + }; + } else if (valuesDS.getNumLit() != null) { + return new XDDFNumericalDataSource() { + private final CTNumData values = (CTNumData) valuesDS.getNumLit().copy(); + private String formatCode = values.isSetFormatCode() ? values.getFormatCode() : null; + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + + @Override + public boolean isCellRange() { + return false; + } + + @Override + public boolean isLiteral() { + return true; + } + + @Override + public boolean isNumeric() { + return true; + } + + @Override + public boolean isReference() { + return false; + } + + @Override + public int getPointCount() { + return (int) values.getPtCount().getVal(); + } + + @Override + public Double getPointAt(int index) { + return Double.valueOf(values.getPtArray(index).getV()); + } + + @Override + public String getDataRangeReference() { + return null; + } + + @Override + public int getColIndex() { + return 0; + } + }; + } else { + return null; // in some weird cases the element is empty + } + } + + public static XDDFNumericalDataSource fromArray(T[] elements) { + return new LiteralNumericalArrayDataSource<>(elements); + } + + public static XDDFCategoryDataSource fromArray(String[] elements) { + return new LiteralStringArrayDataSource(elements); + } + + public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange) { + return new NumericalArrayDataSource<>(elements, dataRange); + } + + public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange) { + return new StringArrayDataSource(elements, dataRange); + } + + public static XDDFNumericalDataSource fromArray(T[] elements, String dataRange, int col) { + return new NumericalArrayDataSource<>(elements, dataRange, col); + } + + public static XDDFCategoryDataSource fromArray(String[] elements, String dataRange, int col) { + return new StringArrayDataSource(elements, dataRange, col); + } + + public static XDDFNumericalDataSource fromNumericCellRange(XSSFSheet sheet, + CellRangeAddress cellRangeAddress) { + return new NumericalCellRangeDataSource(sheet, cellRangeAddress); + } + + public static XDDFCategoryDataSource fromStringCellRange(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + return new StringCellRangeDataSource(sheet, cellRangeAddress); + } + + private abstract static class AbstractArrayDataSource implements XDDFDataSource { + private final T[] elements; + private final String dataRange; + private int col = 0; + + public AbstractArrayDataSource(T[] elements, String dataRange) { + this.elements = elements.clone(); + this.dataRange = dataRange; + } + + public AbstractArrayDataSource(T[] elements, String dataRange, int col) { + this.elements = elements.clone(); + this.dataRange = dataRange; + this.col = col; + } + + @Override + public int getPointCount() { + return elements.length; + } + + @Override + public T getPointAt(int index) { + return elements[index]; + } + + @Override + public boolean isCellRange() { + return false; + } + + @Override + public boolean isReference() { + return dataRange != null; + } + + @Override + public boolean isNumeric() { + Class arrayComponentType = elements.getClass().getComponentType(); + return (Number.class.isAssignableFrom(arrayComponentType)); + } + + /** + * @return the dataRange + * @throws UnsupportedOperationException if there is no Data Range Reference + */ + @Override + public String getDataRangeReference() { + if (dataRange == null) { + throw new UnsupportedOperationException("Literal data source can not be expressed by reference."); + } else { + return dataRange; + } + } + + @Override + public int getColIndex() { + return col; + } + } + + private static class NumericalArrayDataSource extends AbstractArrayDataSource + implements XDDFNumericalDataSource { + private String formatCode; + + public NumericalArrayDataSource(T[] elements, String dataRange) { + super(elements, dataRange); + } + + public NumericalArrayDataSource(T[] elements, String dataRange, int col) { + super(elements, dataRange, col); + } + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + } + + private static class StringArrayDataSource extends AbstractArrayDataSource + implements XDDFCategoryDataSource { + public StringArrayDataSource(String[] elements, String dataRange) { + super(elements, dataRange); + } + + public StringArrayDataSource(String[] elements, String dataRange, int col) { + super(elements, dataRange, col); + } + + @Override + public String getFormatCode() { + return null; + } + } + + private static class LiteralNumericalArrayDataSource extends NumericalArrayDataSource { + public LiteralNumericalArrayDataSource(T[] elements) { + super(elements, null, 0); + } + + @Override + public boolean isLiteral() { + return true; + } + } + + private static class LiteralStringArrayDataSource extends StringArrayDataSource { + public LiteralStringArrayDataSource(String[] elements) { + super(elements, null, 0); + } + + @Override + public boolean isLiteral() { + return true; + } + } + + private abstract static class AbstractCellRangeDataSource implements XDDFDataSource { + private final XSSFSheet sheet; + private final CellRangeAddress cellRangeAddress; + private final int numOfCells; + private final XSSFFormulaEvaluator evaluator; + + protected AbstractCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + this.sheet = sheet; + // Make copy since CellRangeAddress is mutable. + this.cellRangeAddress = cellRangeAddress.copy(); + this.numOfCells = this.cellRangeAddress.getNumberOfCells(); + this.evaluator = sheet.getWorkbook().getCreationHelper().createFormulaEvaluator(); + } + + @Override + public int getPointCount() { + return numOfCells; + } + + @Override + public boolean isCellRange() { + return true; + } + + @Override + public boolean isReference() { + return true; + } + + @Override + public int getColIndex() { + return cellRangeAddress.getFirstColumn(); + } + + @Override + public String getDataRangeReference() { + return cellRangeAddress.formatAsString(sheet.getSheetName(), true); + } + + protected CellValue getCellValueAt(int index) { + if (index < 0 || index >= numOfCells) { + throw new IndexOutOfBoundsException( + "Index must be between 0 and " + (numOfCells - 1) + " (inclusive), given: " + index); + } + int firstRow = cellRangeAddress.getFirstRow(); + int firstCol = cellRangeAddress.getFirstColumn(); + int lastCol = cellRangeAddress.getLastColumn(); + int width = lastCol - firstCol + 1; + int rowIndex = firstRow + index / width; + int cellIndex = firstCol + index % width; + XSSFRow row = sheet.getRow(rowIndex); + return (row == null) ? null : evaluator.evaluate(row.getCell(cellIndex)); + } + } + + private static class NumericalCellRangeDataSource extends AbstractCellRangeDataSource + implements XDDFNumericalDataSource { + protected NumericalCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + super(sheet, cellRangeAddress); + } + + private String formatCode; + + @Override + public String getFormatCode() { + return formatCode; + } + + @Override + public void setFormatCode(String formatCode) { + this.formatCode = formatCode; + } + + @Override + public Double getPointAt(int index) { + CellValue cellValue = getCellValueAt(index); + if (cellValue != null && cellValue.getCellType() == CellType.NUMERIC) { + return cellValue.getNumberValue(); + } else { + return null; + } + } + + @Override + public boolean isNumeric() { + return true; + } + } + + private static class StringCellRangeDataSource extends AbstractCellRangeDataSource + implements XDDFCategoryDataSource { + protected StringCellRangeDataSource(XSSFSheet sheet, CellRangeAddress cellRangeAddress) { + super(sheet, cellRangeAddress); + } + + @Override + public String getPointAt(int index) { + CellValue cellValue = getCellValueAt(index); + if (cellValue != null && cellValue.getCellType() == CellType.STRING) { + return cellValue.getStringValue(); + } else { + return null; + } + } + + @Override + public boolean isNumeric() { + return false; + } + + @Override + public String getFormatCode() { + return null; + } + } + + public static class XDDFCategoryDataSourceImpl implements XDDFCategoryDataSource { + CTAxDataSource categoryDS; + private final CTStrData categoryString; + private final CTNumData categoryNumber; + private final String formatCode; + + enum Type { + FROM_NUMBER_REFERENCE, FROM_STRING_REFERENCE, FROM_STRING_LITERAL, FROM_NUMBER_LITERAL, ERROR + } + + public final Type type; + + public XDDFCategoryDataSourceImpl(CTAxDataSource categoryDS) { + this.categoryDS = categoryDS; + if (categoryDS.getNumRef() != null && categoryDS.getNumRef().getNumCache() != null) { + type = Type.FROM_NUMBER_REFERENCE; + categoryNumber = (CTNumData) categoryDS.getNumRef().getNumCache().copy(); + categoryString = null; + } else if (categoryDS.getStrRef() != null && categoryDS.getStrRef().getStrCache() != null) { + type = Type.FROM_STRING_REFERENCE; + categoryString = (CTStrData) categoryDS.getStrRef().getStrCache().copy(); + categoryNumber = null; + } else if (categoryDS.getNumLit() != null) { + type = Type.FROM_NUMBER_LITERAL; + categoryNumber = (CTNumData) categoryDS.getNumLit().copy(); + categoryString = null; + } else if (categoryDS.getStrLit() != null) { + type = Type.FROM_STRING_LITERAL; + this.categoryDS = categoryDS; + categoryString = (CTStrData) categoryDS.getStrLit().copy(); + categoryNumber = null; + } else { + categoryNumber = null; + categoryString = null; + type = Type.ERROR; // in some weird cases the element is empty + } + if (type == Type.FROM_NUMBER_LITERAL || type == Type.FROM_NUMBER_REFERENCE) + formatCode = categoryNumber.isSetFormatCode() ? categoryNumber.getFormatCode() : null; + else + formatCode = null; + } + + @Override + public int getPointCount() { + if (categoryString != null) { + return (int) categoryString.getPtCount().getVal(); + } + return (int) categoryNumber.getPtCount().getVal(); + } + + @Override + public String getPointAt(int index) { + if (categoryString != null) + return categoryString.getPtArray(index).getV(); + return categoryNumber.getPtArray(index).getV(); + } + + @Override + public boolean isCellRange() { + return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_STRING_REFERENCE); + } + + public boolean isReference() { + return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_STRING_REFERENCE); + } + + public String getDataRangeReference() { + switch (type) { + case FROM_NUMBER_REFERENCE: + return categoryDS.getNumRef().getF(); + case FROM_STRING_REFERENCE: + return categoryDS.getStrRef().getF(); + } + return null; + } + + public String getFormatCode() { + return formatCode; + } + + public boolean isNumeric() { + return (type == Type.FROM_NUMBER_REFERENCE || type == Type.FROM_NUMBER_LITERAL); + } + + public boolean isLiteral() { return (type == Type.FROM_STRING_LITERAL || type == Type.FROM_NUMBER_LITERAL); - } + } - } + } }