I want to add row in excel by java POI and I try with both shiftRows() function and createRow() function
both function can add row in excel but below chart position is remain and not move
I also like to move (shift down) the position of chart
I use poi version 3.9
Can anyone give me the advice or idea to move the position of that chart image
As the fact, the data range of chart also not changed. I need not only to move the position of charts but also need to increase the data range of chart
thanks!!
The shifting of the drawing anchors which determine the chart positions is possible. The method void insertRowsShiftShapes(Sheet sheet, int startRow, int n) does this for all drawing anchors which are affected of row inserting process into the sheet.
The correcting of the chart data ranges which are affected of the row inserting into the sheet is complicated as said already. It is not well tested and not ready yet. But I will provide it as a working draft. I hope it is a useful start point for further programming.
For running the code the ooxml-schemas-1.3.jar is needed as mentioned in apache poi FAQ
A good resource for documentation of the ooxml-schema objects for me is grepcode
Examples: CTTwoCellAnchor, CTPieChart, CTPieSer
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import java.io.*;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer;
import java.util.List;
class InsertRowsAboveChart {
//a method for shift rows and shift all anchors in drawing below the shifted rows
private static void insertRowsShiftShapes(Sheet sheet, int startRow, int n) {
java.util.List<CTTwoCellAnchor> drawingAnchors = ((XSSFDrawing)sheet.getDrawingPatriarch()).getCTDrawing().getTwoCellAnchorList();
for (CTTwoCellAnchor drawingAnchor : drawingAnchors) {
int fromRow = drawingAnchor.getFrom().getRow();
int toRow = drawingAnchor.getTo().getRow();
if (fromRow >= startRow) {
drawingAnchor.getFrom().setRow(fromRow + n);
drawingAnchor.getTo().setRow(toRow + n);
}
}
sheet.shiftRows(startRow, sheet.getLastRowNum(), n);
correctDataRangesOfCharts(sheet, startRow, n);
}
//a method for correcting data ranges for charts which are affected of the shifted rows
//!!working draft, not ready yet!!
private static void correctDataRangesOfCharts(Sheet sheet, int startRow, int n) {
java.util.List<XSSFChart> charts = ((XSSFDrawing)sheet.getDrawingPatriarch()).getCharts();
for (XSSFChart chart : charts) {
//pie charts
java.util.List<CTPieChart> piecharts = chart.getCTChart().getPlotArea().getPieChartList();
for (CTPieChart piechart : piecharts) {
java.util.List<CTPieSer> pieseries = piechart.getSerList();
for (CTPieSer pieserie : pieseries) {
boolean strRefchanged = false;
if (pieserie.getCat().isSetMultiLvlStrRef()) {
String strRef = pieserie.getCat().getMultiLvlStrRef().getF();
//todo: this only corrects the end row of the ranges, should also correct start row if affected
int strRefEndRow = Integer.parseInt(strRef.substring(strRef.lastIndexOf('$') + 1));
if (strRefEndRow >= startRow) {
strRef = strRef.substring(0, strRef.lastIndexOf('$') +1) + (strRefEndRow + n);
pieserie.getCat().getMultiLvlStrRef().setF(strRef);
strRefchanged = true;
}
} else if (pieserie.getCat().isSetStrRef()) {
String strRef = pieserie.getCat().getStrRef().getF();
int strRefEndRow = Integer.parseInt(strRef.substring(strRef.lastIndexOf('$') + 1));
if (strRefEndRow >= startRow) {
strRef = strRef.substring(0, strRef.lastIndexOf('$') +1) + (strRefEndRow + n);
pieserie.getCat().getStrRef().setF(strRef);
strRefchanged = true;
}
}
if (strRefchanged) {
String numRef = pieserie.getVal().getNumRef().getF();
int numRefEndRow = Integer.parseInt(numRef.substring(numRef.lastIndexOf('$') + 1));
if (numRefEndRow >= startRow) {
numRef = numRef.substring(0, numRef.lastIndexOf('$') +1) + (numRefEndRow + n);
pieserie.getVal().getNumRef().setF(numRef);
}
}
}
}
//pie charts end
}
}
public static void main(String[] args) {
try {
InputStream inp = new FileInputStream("Workbook.xlsx");
Workbook wb = WorkbookFactory.create(inp);
Sheet sheet = wb.getSheetAt(0);
//sheet.shiftRows(3, 5, 4);
insertRowsShiftShapes(sheet, 2, 4);
FileOutputStream fileOut = new FileOutputStream("Workbook.xlsx");
wb.write(fileOut);
wb.close();
} catch (InvalidFormatException ifex) {
} catch (FileNotFoundException fnfex) {
} catch (IOException ioex) {
}
}
}
Related
I need to use Apache POI(4.1.1) to write some text inside a textbox (or rectangle shape), and this text must have superscripts in it. I can get this to work for data inside a cell, but not when using a textbox. Here is a minimal example:
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.*;
import java.io.FileOutputStream;
public class SuperScriptTest {
public static void main(String[] args) throws Exception {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet ws = wb.createSheet("Sheet 1");
XSSFFont fontInfo = wb.createFont();
fontInfo.setFontName("Arial");
fontInfo.setFontHeightInPoints((short) 11);
XSSFFont fontInfoSuperscript = wb.createFont();
fontInfoSuperscript.setFontName("Arial");
fontInfoSuperscript.setFontHeightInPoints((short) 11);
fontInfoSuperscript.setTypeOffset(Font.SS_SUPER);
fontInfoSuperscript.setColor(IndexedColors.RED.getIndex());
Row row = ws.createRow(0);
Cell cell = row.createCell(0);
// Writing to a cell produces desired results:
XSSFRichTextString richString = new XSSFRichTextString("Level3");
richString.applyFont(fontInfo);
richString.applyFont(5, 6, fontInfoSuperscript);
cell.setCellValue(richString);
// Writing to a textbox does not:
XSSFDrawing drawing = ws.createDrawingPatriarch();
XSSFTextBox txtBox = drawing.createTextbox(new XSSFClientAnchor(0, 0, 0, 0, 0, 5, 2, 7));
XSSFRichTextString richString2 = new XSSFRichTextString("Level3");
richString2.applyFont(fontInfo);
richString2.applyFont(5, 6, fontInfoSuperscript);
txtBox.setText(richString2);
try (FileOutputStream fileOut = new FileOutputStream("Superscript.xlsx")) {
wb.write(fileOut);
}
}
}
The cell will give me the right font and size, and properly superscript the 3 and turn it red.
The textbox will give me the correct font and size, and will color the 3, but it will not superscript the 3.
Thank you for any help.
In Excel cells and text box shapes have different kinds of font settings. For Excel cells spreadsheet settings are used while for text box shapes drawing settings are used.
Because XSSFRichTextString mostly gets used for Excel cell values and shared strings, it internally uses spreadsheet settings. When it comes to XSSFRichTextStrings in context of shapes, the settings need to be converted. This is done in XSSFSimpleShape.setText(XSSFRichTextString str) using the method XSSFSimpleShape.applyAttributes(CTRPrElt pr, CTTextCharacterProperties rPr).
In spreadsheet settings CTRPrElt there is a CTVerticalAlignFontProperty used to set baseline, superscript or subscript. In drawing settings CTTextCharacterProperties there is a baseline attrtibute used which is 0 for baseline, +n% for superscript and -n% for subscript. There n% is the distance from the baseline.
Until now the XSSFSimpleShape.applyAttributes lacks converting CTVerticalAlignFontProperty to CTTextCharacterProperties.setBaseline. To get this, one could patching XSSFSimpleShape.applyAttributes like so:
private static void applyAttributes(CTRPrElt pr, CTTextCharacterProperties rPr) {
...
if (pr.sizeOfVertAlignArray() > 0) {
CTVerticalAlignFontProperty vertAlign = pr.getVertAlignArray(0);
if (vertAlign.getVal() == STVerticalAlignRun.BASELINE) {
rPr.setBaseline(0);
} else if (vertAlign.getVal() == STVerticalAlignRun.SUPERSCRIPT) {
rPr.setBaseline(30000); //30% distance from baseline == default superscript
} else if (vertAlign.getVal() == STVerticalAlignRun.SUBSCRIPT) {
rPr.setBaseline(-25000); //-25% distance from baseline == default subscript
}
}
}
For superscript 30% distance from baseline is choosen because that is the default when someone ticks text effect superscript in text box font settings. For subscript -25% distance from baseline is choosen because that is the default when someone ticks text effect subscript in text box font settings.
Complete example having own setText and applyAttributes to show that it works:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.*;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
import org.apache.poi.hssf.util.HSSFColor;
import java.io.FileOutputStream;
public class SuperScriptTest {
static void setText(XSSFSimpleShape shape, XSSFRichTextString str) {
XSSFWorkbook wb = (XSSFWorkbook) shape.getDrawing().getParent().getParent();
//str.setStylesTableReference(wb.getStylesSource()); // cannot be done here since XSSFRichTextString.setStylesTableReference has protected access
CTTextParagraph p = CTTextParagraph.Factory.newInstance();
if (str.numFormattingRuns() == 0) {
CTRegularTextRun r = p.addNewR();
CTTextCharacterProperties rPr = r.addNewRPr();
rPr.setLang("en-US");
rPr.setSz(1100);
r.setT(str.getString());
} else {
for (int i = 0; i < str.getCTRst().sizeOfRArray(); i++) {
CTRElt lt = str.getCTRst().getRArray(i);
CTRPrElt ltPr = lt.getRPr();
if (ltPr == null) {
ltPr = lt.addNewRPr();
}
CTRegularTextRun r = p.addNewR();
CTTextCharacterProperties rPr = r.addNewRPr();
rPr.setLang("en-US");
applyAttributes(ltPr, rPr);
r.setT(lt.getT());
}
}
//clearText(); //replaced by it's code, 3 lines below
shape.getTextParagraphs().clear();
CTTextBody txBody = shape.getCTShape().getTxBody();
txBody.setPArray(null); // remove any existing paragraphs
CTShape ctShape = shape.getCTShape();
ctShape.getTxBody().setPArray(new CTTextParagraph[] { p });
//shape.getTextParagraphs().add(new XSSFTextParagraph(ctShape.getTxBody().getPArray(0), ctShape)); // cannot be done here since XSSFTextParagraph contructor is not public
}
static void applyAttributes(CTRPrElt pr, CTTextCharacterProperties rPr) {
if (pr.sizeOfBArray() > 0) {
rPr.setB(pr.getBArray(0).getVal());
}
if (pr.sizeOfUArray() > 0) {
STUnderlineValues.Enum u1 = pr.getUArray(0).getVal();
if (u1 == STUnderlineValues.SINGLE) {
rPr.setU(STTextUnderlineType.SNG);
} else if (u1 == STUnderlineValues.DOUBLE) {
rPr.setU(STTextUnderlineType.DBL);
} else if (u1 == STUnderlineValues.NONE) {
rPr.setU(STTextUnderlineType.NONE);
}
}
if (pr.sizeOfIArray() > 0) {
rPr.setI(pr.getIArray(0).getVal());
}
if (pr.sizeOfRFontArray() > 0) {
CTTextFont rFont = rPr.isSetLatin() ? rPr.getLatin() : rPr.addNewLatin();
rFont.setTypeface(pr.getRFontArray(0).getVal());
}
if (pr.sizeOfSzArray() > 0) {
int sz = (int) (pr.getSzArray(0).getVal() * 100);
rPr.setSz(sz);
}
if (pr.sizeOfColorArray() > 0) {
CTSolidColorFillProperties fill = rPr.isSetSolidFill() ? rPr.getSolidFill() : rPr.addNewSolidFill();
org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor xlsColor = pr.getColorArray(0);
if (xlsColor.isSetRgb()) {
CTSRgbColor clr = fill.isSetSrgbClr() ? fill.getSrgbClr() : fill.addNewSrgbClr();
clr.setVal(xlsColor.getRgb());
} else if (xlsColor.isSetIndexed()) {
HSSFColor indexed = HSSFColor.getIndexHash().get((int) xlsColor.getIndexed());
if (indexed != null) {
byte[] rgb = new byte[3];
rgb[0] = (byte) indexed.getTriplet()[0];
rgb[1] = (byte) indexed.getTriplet()[1];
rgb[2] = (byte) indexed.getTriplet()[2];
CTSRgbColor clr = fill.isSetSrgbClr() ? fill.getSrgbClr() : fill.addNewSrgbClr();
clr.setVal(rgb);
}
}
}
if (pr.sizeOfVertAlignArray() > 0) {
CTVerticalAlignFontProperty vertAlign = pr.getVertAlignArray(0);
if (vertAlign.getVal() == STVerticalAlignRun.BASELINE) {
rPr.setBaseline(0);
} else if (vertAlign.getVal() == STVerticalAlignRun.SUPERSCRIPT) {
rPr.setBaseline(30000); //30% distance from baseline == default superscript
} else if (vertAlign.getVal() == STVerticalAlignRun.SUBSCRIPT) {
rPr.setBaseline(-25000); //-25% distance from baseline == default subscript
}
}
}
public static void main(String[] args) throws Exception {
XSSFWorkbook wb = new XSSFWorkbook();
XSSFSheet ws = wb.createSheet("Sheet 1");
XSSFFont fontInfo = wb.createFont();
fontInfo.setFontName("Arial");
fontInfo.setFontHeightInPoints((short) 11);
XSSFFont fontInfoSuperscript = wb.createFont();
fontInfoSuperscript.setFontName("Arial");
fontInfoSuperscript.setFontHeightInPoints((short) 11);
fontInfoSuperscript.setTypeOffset(Font.SS_SUPER);
//fontInfoSuperscript.setTypeOffset(Font.SS_SUB);
fontInfoSuperscript.setColor(IndexedColors.RED.getIndex());
Row row = ws.createRow(0);
Cell cell = row.createCell(0);
// Writing to a cell produces desired results:
XSSFRichTextString richString = new XSSFRichTextString("Level3");
richString.applyFont(fontInfo);
richString.applyFont(5, 6, fontInfoSuperscript);
cell.setCellValue(richString);
// Writing to a textbox does not:
XSSFDrawing drawing = ws.createDrawingPatriarch();
XSSFTextBox txtBox = drawing.createTextbox(new XSSFClientAnchor(0, 0, 0, 0, 0, 5, 2, 7));
XSSFRichTextString richString2 = new XSSFRichTextString("Level3");
richString2.applyFont(fontInfo);
richString2.applyFont(5, 6, fontInfoSuperscript);
//txtBox.setText(richString2);
setText(txtBox, richString2);
try (FileOutputStream fileOut = new FileOutputStream("Superscript.xlsx")) {
wb.write(fileOut);
}
}
}
I created code as below to create excel file and calculate and get cell value after because I need to use function in excel.(this function is not supported by apache poi, so I think I have to read cached./formatted value)
package main;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class WorkbookEvaluator
{
public static void pop_mean()
{
System.out.println ("Test population mean in two different populations are same or not");
System.out.println ("This program works only for two-tailed ");
Scanner in = new Scanner(System.in);
System.out.println ("What is population mean?:");
double m = in.nextDouble();
System.out.println ("How many samples are taken from population?:");
double n = in.nextDouble();
System.out.println ("What is Sample mean?:");
double X = in.nextDouble();
System.out.println ("What is unbiased variance for population:");
double U = in.nextDouble();
System.out.println ("What is Level of Significance (Type with %-value)");
double L = in.nextDouble();
double l = L/100;
double a = n-1;
double b = X-m;
double c = Math.sqrt(n);
double d = Math.sqrt(U);
double f = d/c;
double T = b/f;
System.out.println ("Degree of freedom is " + a);
System.out.println ("Test statistic is " + T);
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet();
Row row1 = sheet.createRow(1);
Row row2 = sheet.createRow(2);
Cell cell1_1 = row1.createCell(1);
Cell cell1_2 = row1.createCell(2);
Cell cell1_3 = row1.createCell(3);
Cell cell2_3 = row2.createCell(3);
Cell cell2_4 = row2.createCell(4);
cell1_1.setCellValue(l);
cell1_2.setCellValue(a);
cell2_3.setCellFormula("_xlfn.T.INV.2T(" + l +"," + a + ")");
cell2_4.setCellFormula("SUM(" + l +"," + a + ")");
FileOutputStream out = null;
try {
out = new FileOutputStream("T-inverse.xlsx");
wb.write(out);
} catch(IOException e) {
System.out.println(e.toString());
} finally {
try {
out.close();
} catch(IOException e) {
System.out.println(e.toString());
}
}
}
public static void read_excel() throws IOException
{
for (int q=3;q<5;q++)
{
XSSFWorkbook book = new XSSFWorkbook("C:\\Users\\shump\\Java\\Population mean Test\\T-inverse.xlsx");
book.setForceFormulaRecalculation(true);
XSSFSheet sheet = book.getSheetAt(0);
sheet.setForceFormulaRecalculation(true);
XSSFRow row = sheet.getRow(2);
final DataFormatter dataFormatter = new DataFormatter();
final double formtatedValue = row.getCell((short) q).getNumericCellValue();
System.out.println(formtatedValue);
}
}
public static void main(String[] args) throws IOException
{
pop_mean();
read_excel();
}
}
As can be see from the attempted picture below, it works successfully in excel. However, consoled output will display 0.0. I thought this is because of function is not supported so I tried same thing by using sum function which is one of the basic function and this function is supported. However, it didn't work. In picture D3 is where I used T.INV.2T function.
Test population mean in two different populations are same or not
This program works only for two-tailed
What is population mean?:
171.4
How many samples are taken from population?:
9
What is Sample mean?:
172.8
What is unbiased variance for population:
4
What is Level of Significance (Type with %-value)
5
Degree of freedom is 8.0
Test statistic is 2.1000000000000085
0.0
0.0
After few research, I found article which states when the formula value is not cached, output will be 0. So, please tell me how I can cache the value or is there any other way that I can take to get value of non-supported function.
You seems misunderstand what apache poi is made for. In terms of Excel it is made for creating Excel files. That meams it creates files which Excelis able to open then. When it opens workbook files, it opens them as apache poi Workbook only to be able to append content. Apache poi is not thought to be the same as Excel and it does not interact with any Excel application.
So the setForceFormulaRecalculation is not a apache poi setting but a setting for Excel when Excel opens the workbook. If setForceFormulaRecalculation is set true, then Excel recalculates all formulas when Excel opens the workbook file. It does not mean that apache poi recalculates all formulas.
To force apache poi calculating the formulas, FormulaEvaluator needs to be used.
For example if you extend your code as follows:
...
import org.apache.poi.ss.usermodel.FormulaEvaluator;
...
Workbook wb = new XSSFWorkbook();
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
...
cell2_4.setCellFormula("SUM(" + l +"," + a + ")");
try {
evaluator.evaluateFormulaCell(cell2_4);
} catch (org.apache.poi.ss.formula.eval.NotImplementedException notImplEx) {
notImplEx.printStackTrace();
}
...
then the SUM formula gets evaluated and cell2_4 contains the evaluated numeric value additional to the formula.
But of course
...
cell2_3.setCellFormula("TINV(" + l +"," + a + ")");
try {
evaluator.evaluateFormulaCell(cell2_3);
} catch (org.apache.poi.ss.formula.eval.NotImplementedException notImplEx) {
notImplEx.printStackTrace();
}
...
will lead to NotImplementedException since TINV is not yet implemented in apache poi.
So we need to do what is shown in Developing Formula Evaluation. In terms of the TINV function this would be:
...
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.eval.*;
import org.apache.poi.ss.formula.functions.*;
import org.apache.commons.math3.distribution.TDistribution;
...
static Function TINV = new Fixed2ArgFunction() {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2) {
try {
ValueEval ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex);
double dArg1 = OperandResolver.coerceValueToDouble(ve1);
ValueEval ve2 = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex);
double dArg2 = OperandResolver.coerceValueToDouble(ve2);
TDistribution t = new TDistribution(dArg2);
double result = t.inverseCumulativeProbability(1d - dArg1/2d);
if (Double.isNaN(result) || Double.isInfinite(result)) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
}
return new NumberEval(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
}
};
...
and then
...
WorkbookEvaluator.registerFunction("TINV", TINV);
...
Note, I have implemented TINV instead of _xlfn.T.INV.2T since the latter is not able to be implemented that way because of it's strange name. All Excel versions I know also support TINV instead of _xlfn.T.INV.2T.
Complete example extending your code:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
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.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.formula.WorkbookEvaluator;
import org.apache.poi.ss.formula.eval.*;
import org.apache.poi.ss.formula.functions.*;
import org.apache.commons.math3.distribution.TDistribution;
public class WorkbookEvaluatorTest {
static Function TINV = new Fixed2ArgFunction() {
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg1, ValueEval arg2) {
try {
ValueEval ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex);
double dArg1 = OperandResolver.coerceValueToDouble(ve1);
ValueEval ve2 = OperandResolver.getSingleValue(arg2, srcRowIndex, srcColumnIndex);
double dArg2 = OperandResolver.coerceValueToDouble(ve2);
TDistribution t = new TDistribution(dArg2);
double result = t.inverseCumulativeProbability(1d - dArg1/2d);
if (Double.isNaN(result) || Double.isInfinite(result)) {
throw new EvaluationException(ErrorEval.NUM_ERROR);
}
return new NumberEval(result);
} catch (EvaluationException e) {
return e.getErrorEval();
}
}
};
public static void pop_mean() {
WorkbookEvaluator.registerFunction("TINV", TINV);
System.out.println ("Test population mean in two different populations are same or not");
System.out.println ("This program works only for two-tailed ");
Scanner in = new Scanner(System.in);
System.out.println ("What is population mean?:");
double m = in.nextDouble();
System.out.println ("How many samples are taken from population?:");
double n = in.nextDouble();
System.out.println ("What is Sample mean?:");
double X = in.nextDouble();
System.out.println ("What is unbiased variance for population:");
double U = in.nextDouble();
System.out.println ("What is Level of Significance (Type with %-value)");
double L = in.nextDouble();
double l = L/100;
double a = n-1;
double b = X-m;
double c = Math.sqrt(n);
double d = Math.sqrt(U);
double f = d/c;
double T = b/f;
System.out.println ("Degree of freedom is " + a);
System.out.println ("Test statistic is " + T);
Workbook wb = new XSSFWorkbook();
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
Sheet sheet = wb.createSheet();
Row row1 = sheet.createRow(1);
Row row2 = sheet.createRow(2);
Cell cell1_1 = row1.createCell(1);
Cell cell1_2 = row1.createCell(2);
Cell cell1_3 = row1.createCell(3);
Cell cell2_3 = row2.createCell(3);
Cell cell2_4 = row2.createCell(4);
cell1_1.setCellValue(l);
cell1_2.setCellValue(a);
cell2_3.setCellFormula("TINV(" + l +"," + a + ")");
try {
evaluator.evaluateFormulaCell(cell2_3);
} catch (org.apache.poi.ss.formula.eval.NotImplementedException notImplEx) {
notImplEx.printStackTrace();
}
cell2_4.setCellFormula("SUM(" + l +"," + a + ")");
try {
evaluator.evaluateFormulaCell(cell2_4);
} catch (org.apache.poi.ss.formula.eval.NotImplementedException notImplEx) {
notImplEx.printStackTrace();
}
FileOutputStream out = null;
try {
out = new FileOutputStream("T-inverse.xlsx");
wb.write(out);
} catch(IOException e) {
System.out.println("Write: " + e.toString());
} finally {
try {
out.close();
wb.close();
} catch(IOException e) {
System.out.println("Close: " + e.toString());
}
}
}
public static void read_excel() throws IOException {
for (int q=3;q<5;q++) {
XSSFWorkbook book = new XSSFWorkbook("T-inverse.xlsx");
//book.setForceFormulaRecalculation(true);
XSSFSheet sheet = book.getSheetAt(0);
//sheet.setForceFormulaRecalculation(true);
XSSFRow row = sheet.getRow(2);
final DataFormatter dataFormatter = new DataFormatter();
final double formtatedValue = row.getCell((short) q).getNumericCellValue();
System.out.println(formtatedValue);
}
}
public static void main(String[] args) throws IOException {
pop_mean();
read_excel();
}
}
Currently, this program will run down a column of URLs and output the selected data to the neighboring cell. I can set which column it starts on, but that is all I can do. Right now, I only have it working on one column. How can I instruct it to go to say, column 4 (Column E) and work top down once it is through with column 0 (A)? And then perhaps another, say column J after that?
I believe my problem lies within the "while (!(cell = sheet.getCell..." line, but I am unsure of what to change there without breaking the program.
My code is as follows:
public class App {
private static final int URL_COLUMN = 0; // Column A
private static final int PRICE_COLUMN = 1; //Column B
public static void main(final String[] args) throws Exception {
Workbook originalWorkbook = Workbook.getWorkbook(new File("C:/Users/Shadow/Desktop/original.xls"));
WritableWorkbook workbook = Workbook.createWorkbook(new File("C:/Users/Shadow/Desktop/updated.xls"), originalWorkbook);
originalWorkbook.close();
WritableSheet sheet = workbook.getSheet(0);
int currentRow = 1;
Cell cell;
while (!(cell = sheet.getCell(URL_COLUMN, currentRow)).getType().equals(CellType.EMPTY)) {
String url = cell.getContents();
System.out.println("Checking URL: " + url);
if (url.contains("scrapingsite1.com")) {
String Price = ScrapingSite1(url);
System.out.println("Scraping Site1's Price: " + Price);
Label cellWithPrice = new Label(PRICE_COLUMN, currentRow, Price);
sheet.addCell(cellWithPrice);
}
currentRow++;
}
workbook.write();
workbook.close();
}
private static String ScrapingSite1 (String url) throws IOException {
Document doc = null;
for (int i=1; i <= 6; i++) {
try {
doc = Jsoup.connect(url).userAgent("Mozilla/5.0").timeout(6000).validateTLSCertificates(false).get();
break;
} catch (IOException e) {
System.out.println("Jsoup issue occurred " + i + " time(s).");
}
}
if (doc == null){
return null;
}
else{
return doc.select("p.price").text();
}
}
}
To simplify the code I made an assumption that the price comes always to the next column (+1).
Also to process few columns instead of using single value int URL_COLUMN = 0 I replaced it with array of columns to process: int[] URL_COLUMNS = { 0, 4, 9 }; // Columns A, E, J.
You can then loop over every column {0, 4, 9} and save the data to the next column {1, 5, 10}.
private static final int[] URL_COLUMNS = { 0, 4, 9 }; // Columns A, E, J
public static void main(final String[] args) throws Exception {
Workbook originalWorkbook = Workbook.getWorkbook(new File("C:/Users/Shadow/Desktop/original.xls"));
WritableWorkbook workbook = Workbook.createWorkbook(new File("C:/Users/Shadow/Desktop/updated.xls"), originalWorkbook);
originalWorkbook.close();
WritableSheet sheet = workbook.getSheet(0);
Cell cell;
// loop over every column
for (int i = 0; i < URL_COLUMNS.length; i++) {
int currentRow = 1;
while (!(cell = sheet.getCell(URL_COLUMNS[i], currentRow)).getType().equals(CellType.EMPTY)) {
String url = cell.getContents();
System.out.println("Checking URL: " + url);
if (url.contains("scrapingsite1.com")) {
String Price = ScrapingSite1(url);
System.out.println("Scraping Site1's Price: " + Price);
// save price into the next column
Label cellWithPrice = new Label(URL_COLUMNS[i] + 1, currentRow, Price);
sheet.addCell(cellWithPrice);
}
currentRow++;
}
}
workbook.write();
workbook.close();
}
Is there a way to update formula references when copying a formula in Apache POI?
Say in Excel you have in row 1 the formula =A1/B1. If you copy-paste it, say in row 5, the formula becomes =A5/B5.
In Apache POI if you run the lines
r5.getCell(2).setCellType(CellType.FORMULA);
r5.getCell(2).setCellFormula(r1.getCell(2).getCellFormula());
the formula remains =A1/B1.
Your code is not copy/pasting something but gets the formula string from one cell and sets exactly this formula string to another cell. This will not changing the formula string. How even should it do?
So the need is to get the the formula string from one cell and then adjust this formula string to the target cell.
Since apache poi is able to evaluate formulas, it must also be able to parse formulas. The parsing classes are in the packages org.apache.poi.ss.formula and org.apache.poi.ss.formula.ptg.
So we can use those classes to adjust the formula string to the target cell.
Example:
Following Excel workbook:
and following code:
import java.io.FileInputStream;
import org.apache.poi.ss.formula.*;
import org.apache.poi.ss.formula.ptg.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.util.CellAddress;
public class ExcelCopyFormula {
private static String copyFormula(XSSFSheet sheet, String formula, int coldiff, int rowdiff) {
XSSFEvaluationWorkbook workbookWrapper =
XSSFEvaluationWorkbook.create((XSSFWorkbook) sheet.getWorkbook());
Ptg[] ptgs = FormulaParser.parse(formula, workbookWrapper, FormulaType.CELL
, sheet.getWorkbook().getSheetIndex(sheet));
for (int i = 0; i < ptgs.length; i++) {
if (ptgs[i] instanceof RefPtgBase) { // base class for cell references
RefPtgBase ref = (RefPtgBase) ptgs[i];
if (ref.isColRelative())
ref.setColumn(ref.getColumn() + coldiff);
if (ref.isRowRelative())
ref.setRow(ref.getRow() + rowdiff);
}
else if (ptgs[i] instanceof AreaPtgBase) { // base class for range references
AreaPtgBase ref = (AreaPtgBase) ptgs[i];
if (ref.isFirstColRelative())
ref.setFirstColumn(ref.getFirstColumn() + coldiff);
if (ref.isLastColRelative())
ref.setLastColumn(ref.getLastColumn() + coldiff);
if (ref.isFirstRowRelative())
ref.setFirstRow(ref.getFirstRow() + rowdiff);
if (ref.isLastRowRelative())
ref.setLastRow(ref.getLastRow() + rowdiff);
}
}
formula = FormulaRenderer.toFormulaString(workbookWrapper, ptgs);
return formula;
}
public static void main(String[] args) throws Exception {
XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("test.xlsx"));
XSSFSheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
//if (cell.getCellTypeEnum() == CellType.FORMULA) { // up to apache poi version 3
if (cell.getCellType() == CellType.FORMULA) { // since apache poi version 4
CellAddress source = cell.getAddress();
String formula = cell.getCellFormula();
System.out.print(source + "=" + formula);
int rowdiff = 3;
int coldiff = -2;
CellAddress target = new CellAddress(source.getRow() + rowdiff, source.getColumn() + coldiff);
String newformula = copyFormula(sheet, formula, coldiff, rowdiff);
System.out.println("->" + target + "=" + newformula);
}
}
}
workbook.close();
}
}
leads to following output:
E3=C3/D3->C6=A6/B6
E4=$C4/D$4->C7=$C7/B$4
E5=SUM(C3:D5)->C8=SUM(A6:B8)
E6=SUM(C$3:$D6)->C9=SUM(A$3:$D9)
E7=C3+SUM(C3:D7)->C10=A6+SUM(A6:B10)
E8=C$3+SUM($C3:D$8)->C11=A$3+SUM($C6:B$8)
Updated String copyFormula(Sheet sheet, String formula, int coldiff, int rowdiff) method which works for SS that is for HSSFas well as for XSSF:
private static String copyFormula(Sheet sheet, String formula, int coldiff, int rowdiff) {
Workbook workbook = sheet.getWorkbook();
EvaluationWorkbook evaluationWorkbook = null;
if (workbook instanceof HSSFWorkbook) {
evaluationWorkbook = HSSFEvaluationWorkbook.create((HSSFWorkbook) workbook);
} else if (workbook instanceof XSSFWorkbook) {
evaluationWorkbook = XSSFEvaluationWorkbook.create((XSSFWorkbook) workbook);
}
Ptg[] ptgs = FormulaParser.parse(formula, (FormulaParsingWorkbook)evaluationWorkbook,
FormulaType.CELL, sheet.getWorkbook().getSheetIndex(sheet));
for (int i = 0; i < ptgs.length; i++) {
if (ptgs[i] instanceof RefPtgBase) { // base class for cell references
RefPtgBase ref = (RefPtgBase) ptgs[i];
if (ref.isColRelative())
ref.setColumn(ref.getColumn() + coldiff);
if (ref.isRowRelative())
ref.setRow(ref.getRow() + rowdiff);
}
else if (ptgs[i] instanceof AreaPtgBase) { // base class for range references
AreaPtgBase ref = (AreaPtgBase) ptgs[i];
if (ref.isFirstColRelative())
ref.setFirstColumn(ref.getFirstColumn() + coldiff);
if (ref.isLastColRelative())
ref.setLastColumn(ref.getLastColumn() + coldiff);
if (ref.isFirstRowRelative())
ref.setFirstRow(ref.getFirstRow() + rowdiff);
if (ref.isLastRowRelative())
ref.setLastRow(ref.getLastRow() + rowdiff);
}
}
formula = FormulaRenderer.toFormulaString((FormulaRenderingWorkbook)evaluationWorkbook, ptgs);
return formula;
}
Just if you want the answer for the NPOI, I have created the C# version from the #AxelRichter updated answer:
public static string CopyFormula(ISheet sheet, string formula, int coldiff, int rowdiff)
{
var workbook = sheet.Workbook;
IFormulaParsingWorkbook evaluationWorkbook = null;
if (sheet is XSSFWorkbook)
{
evaluationWorkbook = XSSFEvaluationWorkbook.Create(workbook);
}
else if (sheet is HSSFWorkbook)
{
evaluationWorkbook = HSSFEvaluationWorkbook.Create(workbook);
}
else if (sheet is SXSSFWorkbook)
{
evaluationWorkbook = SXSSFEvaluationWorkbook.Create((SXSSFWorkbook)workbook);
}
var ptgs = FormulaParser.Parse(formula, evaluationWorkbook,FormulaType.Cell, sheet.Workbook.GetSheetIndex(sheet));
for (int i = 0; i < ptgs.Length; i++)
{
if (ptgs[i] is RefPtgBase) {
RefPtgBase ref2 = (RefPtgBase)ptgs[i];
if (ref2.IsColRelative)
ref2.Column = ref2.Column + coldiff;
if (ref2.IsRowRelative)
ref2.Row = ref2.Row + rowdiff;
}
else if (ptgs[i] is AreaPtgBase) {
AreaPtgBase ref2 = (AreaPtgBase)ptgs[i];
if (ref2.IsFirstColRelative)
ref2.FirstColumn += coldiff;
if (ref2.IsLastColRelative)
ref2.LastColumn += coldiff;
if (ref2.IsFirstRowRelative)
ref2.FirstRow += rowdiff;
if (ref2.IsLastRowRelative)
ref2.LastRow += rowdiff;
}
}
formula = FormulaRenderer.ToFormulaString((IFormulaRenderingWorkbook)evaluationWorkbook, ptgs);
return formula;
}
I'm writing a program where I need to merge rows in Excel sheet. Currently, I'm able to merge the starting rows, but when coming to the end, I'm unable to know where it is going wrong. Below is my code.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
public class RowsMerge {
public static void main(String[] args) throws IOException {
FileInputStream fin = new FileInputStream(
new File("C:\\D\\Sheets\\Sample Sheets\\dummy.xls"));
HSSFWorkbook workbook = new HSSFWorkbook(fin);
HSSFSheet sheet = workbook.getSheetAt(0);
int row = sheet.getPhysicalNumberOfRows();
String currentLawName, currentCountry, currentAssociate, previousLawName, previousCountry, previousAssociate;
String currentPages, previousPages;
int startIndex = 1, finalIndex = 0, tempNum = 0;
System.out.println(row);
for (int i = 2; i < (row - 1); i++) {
currentAssociate = sheet.getRow(i).getCell(0).toString();
currentLawName = sheet.getRow(i).getCell(1).toString();
currentCountry = sheet.getRow(i).getCell(2).toString();
currentPages = sheet.getRow(i).getCell(3).toString();
previousAssociate = sheet.getRow(i - 1).getCell(0).toString();
previousLawName = sheet.getRow(i - 1).getCell(1).toString();
previousCountry = sheet.getRow(i - 1).getCell(2).toString();
previousPages = sheet.getRow(i - 1).getCell(3).toString();
if (currentAssociate.equals(previousAssociate) && currentCountry.equals(previousCountry)
&& currentLawName.equals(previousLawName) && currentPages.equals(previousPages)) {
finalIndex += 1;
} else {
sendRangeToMergeCells(startIndex, finalIndex, sheet);
startIndex = i;
finalIndex = 0;
}
}
FileOutputStream fileOut = new FileOutputStream("C:\\D\\Sheets\\Sample Sheets\\dummy.xls");
workbook.write(fileOut);
fileOut.close();
}
private static void sendRangeToMergeCells(int startIndex, int finalIndex, HSSFSheet sheet) {
System.out.println(startIndex + "\t" + (startIndex + finalIndex));
CellRangeAddress region = CellRangeAddress
.valueOf("D" + (startIndex + 1) + ":D" + ((startIndex + finalIndex) + 1));
sheet.addMergedRegion(region);
}
}
Below is my Excel Sheet
SourceExcel:
Current output:
Expected output:
Actually your merge logic is fine, it is your break logic (the logic which determines when to merge the rows) that is missing a bit.
If the last row in your spreadsheet matches the previous row, no merge will be performed because execution will follow the first branch of if (currentAssociate.equals(previousAssociate) && ... and the loop ends. You have to test for and execute the merge logic if necessary one last time after the for loop completes.
Add the following after your for loop:
if (finalIndex > 0) {
sendRangeToMergeCells(startIndex, finalIndex, sheet);
}
this will merge the last rows if necessary.