If string contains with get getContents (JXL) - java

UPDATE: POSSIBLE SOLUTION POSTED BELOW BY MYSELF
I need my program to read a cell. If it contains a specific string, lets say the name "Jacob", then it needs to execute a certain static function defined later in the code.
My IF statement will be nested in a while loop.
int currentRow = 1;
Cell cell;
Cell ncell;
while (!(cell = sheet.getCell(URL_COLUMN, currentRow)).getType().equals(CellType.EMPTY)) {
String url = cell.getContents();
System.out.println("Checking URL: " + url);
ncell = sheet.getCell(NAME_COLUMN, currentRow);
ncell.getContents();
ncell.toString();
if (ncell.contains("Jacob")) {
String FAV = JacobFavFood(url); // A static function will be defined later for this
System.out.println("Jacob's favorite food is " + name);
Label cellWithFAV = new Label(FAV_COLUMN, currentRow, FAV);
sheet.addCell(cellWithFAV);
}
currentRow++;
}
workbook.write();
workbook.close();
UPDATE: POSSIBLE SOLUTION POSTED BELOW BY MYSELF

int currentRow = 1;
Cell cell;
Cell ncell;
while (!(cell = sheet.getCell(URL_COLUMN, currentRow)).getType().equals(CellType.EMPTY)) {
String url = cell.getContents();
String NAME = ncell.getcontents();
System.out.println("Checking URL: " + url);
ncell = sheet.getCell(NAME_COLUMN, currentRow);
if (NAME.contains("Jacob")) {
String FAV = JacobFavFood(url); // A static function will be defined later for this
System.out.println("Jacob's favorite food is " + name);
Label cellWithFAV = new Label(FAV_COLUMN, currentRow, FAV);
sheet.addCell(cellWithFAV);
}
currentRow++;
}
workbook.write();
workbook.close();
I forgot I can define the string as was done with the url. This does work, and it is able to call on a static function. I am still wondering why .toString() didn't work, though.

Related

Apache POI: Remove Chart from Word Template file entirely

I am using a Word template with Excel graphs which I want to programmatically manipulate with the Java Apache POI library. For this I also need to be able to conditionally delete a Chart which is stored in this template.
Based on Axel Richters post (Removing chart from PowerPoint slide with Apache POI) I think I am almost there, but when I want to open the updated Word file it gives an error about unreadable content. This is what I have thus far:
PackagePart packagePartChart = xWPFChart.getPackagePart();
PackagePart packagePartWordDoc = xWPFDocument.getPackagePart();
OPCPackage packageWordDoc = packagePartWordDoc.getPackage();
// iterate over all relations the chart has and remove them
for (PackageRelationship chartrelship : packagePartChart.getRelationships()) {
String partname = chartrelship.getTargetURI().toString();
PackagePart part = packageWordDoc.getPartsByName(Pattern.compile(partname)).get(0);
packageWordDoc.removePart(part);
packagePartChart.removeRelationship(chartrelship.getId());
}
// now remove the chart itself from the word doc
Method removeRelation = POIXMLDocumentPart.class.getDeclaredMethod("removeRelation", POIXMLDocumentPart.class);
removeRelation.setAccessible(true);
removeRelation.invoke(xWPFDocument, xWPFChart);
If I unzip the Word file I correctly see that:
the relation between the WordDoc and the Chart are deleted in '\word\ _rels\document.xml.rels'
the chart itself is deleted in folder '\word\charts'
the relations between the documents supporting the Chart itself are deleted in folder '\word\charts\' _rels
the related chart items themselves are deleted:
StyleN / ColorsN in folder '\word\charts' and
Microsoft_Excel_WorksheetN in folder '\word\embeddings'
Anybody any idea on what could be going wrong here?
Indeed the finding of the right paragraph holding the chart was a challenge. In the end, for simplicity sake, I added a one row/one column placeholder table with one cell with a text, let's say 'targetWordString' in it, DIRECTLY before the chart. With the below function I am location the BodyElementID of this table:
private Integer iBodyElementIterator (XWPFDocument wordDoc,String targetWordString) {
Iterator<IBodyElement> iter = wordDoc.getBodyElementsIterator();
Integer bodyElementID = null;
while (iter.hasNext()) {
IBodyElement elem = iter.next();
bodyElementID = wordDoc.getBodyElements().indexOf(elem);
if (elem instanceof XWPFParagraph) {
XWPFParagraph paragraph = (XWPFParagraph) elem;
for (XWPFRun runText : paragraph.getRuns()) {
String text = runText.getText(0);
Core.getLogger("WordExporter").trace("Body Element ID: " + bodyElementID + " Text: " + text);
if (text != null && text.equals(targetWordString)) {
break;
}
}
} else if (elem instanceof XWPFTable) {
if (((XWPFTable) elem).getRow(0) != null && ((XWPFTable) elem).getRow(0).getCell(0) != null) {
// the first cell holds the name via the template
String tableTitle = ((XWPFTable) elem).getRow(0).getCell(0).getText();
if (tableTitle.equals(targetWordString)) {
break;
}
Core.getLogger("WordExporter").trace("Body Element ID: " + bodyElementID + " Text: " + tableTitle);
} else {
Core.getLogger("WordExporter").trace("Body Element ID: " + bodyElementID + " Table removed!");
}
}
else {
Core.getLogger("WordExporter").trace("Body Element ID: " + bodyElementID + " Text: ?");
}
}
return bodyElementID;
}
In the main part of the code I am calling this function to locate the table, then first delete the chart (ID +1) and then the table (ID)
int elementIDToBeRemoved = iBodyElementIterator(xWPFWordDoc,targetWordString);
xWPFWordDoc.removeBodyElement(elementIDToBeRemoved + 1);
xWPFWordDoc.removeBodyElement(elementIDToBeRemoved);
It needs to be in this order, because the ID's are reordered once you delete a number in between, hence deleting the table first, would mean the chart would get that ID in principle.
I did find a more structural solution for it without the use of dummy tables for the positioning. The solution is build out into 2 parts:
Determine relationship id of chart in word document
Remove chart based on relationship id and remove paragraph + runs
Determine relationship id of chart in word document
First I have determined with a boolean variable 'chartUsed' whether the chart needs to be deleted.
if (!chartUsed) {
String chartPartName = xWPFChart.getPackagePart().getPartName().getName();
String ridChartToBeRemoved = null;
// iterate over all relations the chart has and remove them
for (RelationPart relation : wordDoc.getRelationParts()) {
PackageRelationship relShip = relation.getRelationship();
if (relShip.getTargetURI().toString().equals(chartPartName)) {
ridChartToBeRemoved = relShip.getId();
Core.getLogger(logNode).info("Chart with title " + chartTitle + " has relationship id " + relShip.getId());
break;
}
}
removeChartByRelId(wordDoc, ridChartToBeRemoved);
}
(Core.getLogger is an internal API from the Mendix low code platform I use).
At the end I am calling the function removeChartByRelId which will remove the chart if the relation is found within a run of a paragraph:
Remove chart based on relationship id and remove paragraph + runs
private void removeChartByRelId (XWPFDocument wordDoc, String chartRelId) {
Iterator<IBodyElement> iter = wordDoc.getBodyElementsIterator();
Integer bodyElementID = null;
while (iter.hasNext()) {
IBodyElement elem = iter.next();
bodyElementID = wordDoc.getBodyElements().indexOf(elem);
if (elem instanceof XWPFParagraph) {
Core.getLogger(logNode).trace("XWPFParagraph Body Element ID: " + bodyElementID);
XWPFParagraph paragraph = (XWPFParagraph) elem;
boolean removeParagraph = false;
for (XWPFRun run : paragraph.getRuns()) {
if (run.getCTR().getDrawingList().size()>0) {
String drawingXMLText = run.getCTR().getDrawingList().get(0).xmlText();
boolean isChart = drawingXMLText.contains("http://schemas.openxmlformats.org/drawingml/2006/chart");
String graphicDataXMLText = run.getCTR().getDrawingList().get(0).getInlineArray(0).getGraphic().getGraphicData().xmlText();
boolean contains = graphicDataXMLText.contains(chartRelId);
if (isChart && contains) {
Core.getLogger(logNode).info("Removing chart with relId " + chartRelId + " as it isnt used in data");
run.getCTR().getDrawingList().clear();
removeParagraph = true;
}
}
}
if (removeParagraph) {
for (int i = 0; i < paragraph.getRuns().size(); i++){
paragraph.removeRun(i);
}
}
}
}
}

Retrieving and locating cell value in Excel

Hopefully i will explain clear enough, I have a List of objects ( each object have 3 properties ) those objects are printed to xls file, I am trying to locate the cell where the value were assigned first and the last in order to get a references and setFormula to get Sum of all of the middle values
public String getCellReference(int rowPosition, int cellPosition) {
CellReference reference = new CellReference(rowPosition, cellPosition);
return reference.formatAsString();
}
public String getCellReference(int rowPosition, int cellPosition) {
CellReference reference = new CellReference(rowPosition, cellPosition);
return reference.formatAsString();
}
public String getSumFormula(int rowPosition, int cellPosition, int rowPosition2, int cellPosition2) {
String startCell = getCellReference(rowPosition, cellPosition);
String finishCell = getCellReference(rowPosition2, cellPosition2);
return "SUM(" + startCell + ":" + finishCell + ")";
}
To locate first value and last its not a problem but its seems to be a problem for me to locate a cell indexes
public Cell locateCell(List<Invoice> invoices){
BigDecimal firstGeneralTotal = invoices.get(0).generalTotal();
BigDecimal lastGeneralTotal = invoices.get(invoices.size()-1).generalTotal();
}
Basic access using POI would be like this:
XSSFWorkbook wb = new XSSFWorkbook( … );
XSSFSheet sheet = workbook1.getSheetAt(0);
XSSFRow row = sheet.getRow(0);
XSSFCell cell = row.getCell(0);
String value = cell.getStringCellValue();
Instead of using getStringCellValue you can also use one of those methods:
cell.getCellFormula()
cell.getNumericCellValue()
cell.getBooleanCellValue()
cell.getErrorCellValue()
Accordingly, you can do cell.setCellFormula(String formula) to set a cell.
If you want to add a formular to the end of the table containing the sum, you could use the cell references if you like. In this case, you'd need to find the last row in the excel sheet:
sheet.getPhysicalNumberOfRows()
Then you could add a new row to the end of your sheet:
sheet.createRow(sheet.getPhysicalNumberOfRows()+1);
and put your sum in according cell.
Btw, I am not sure if I would use CellReferences, since they can contain references to other sheets as well. If I work only with a single sheet, I'd try to use the index numbers and translate them accordingly to A1:A200 etc.
Does this help you? If I got your question wrong, let me know in the comments, I may update my answer if possible.
public String getCellReference(int rowPosition, int cellPosition) {
CellReference reference = new CellReference(rowPosition, cellPosition);
return reference.formatAsString();
}
public String getCoefficientFormula(int startRow, int firstCell, int endRow, int lastCell) {
String firstReference = getCellReference(startRow, firstCell);
String lastReference = getCellReference(endRow, lastCell);
return firstReference + "/" + lastReference;
}
public String getSumFormula(int startRow, int endRow, int start, int finish) {
String startCell = getCellReference(startRow,start);
String finishCell = getCellReference(endRow,finish);
return "SUM(" + startCell + ":" + finishCell + ")";
}

URL is getting displayed as String in table

I wanted to display one URL in a table.
Value I had was : www.yahoo.com
I converted into format as below and returning back to table :
www.yahoo.com</font>"> Link
I want it to be displayed in URL form. But it is displaying as a string like as it is above.
Below is the method used for conversion :
private static String validateString(String header) throws Exception
{
System.out.println("&&&&&&&&&& inside validateString ");
String displayString = "";
String colorValue = "";
try
{
if((header) != null && !"null".equalsIgnoreCase(header))
{
displayString = header;
colorValue = "red";
displayString = "<font color=\""+colorValue+"\">"+displayString+"</font>";
}
}
catch(Exception e)
{
displayString = " ";
}
if(!"".equals(displayString))
{
displayString = " Link ";
}
if ("".equals(displayString))
{
displayString = " ";
}
return displayString;
}
Please let me know how to display as URL.
Thanks.
In the attribute href for the tag A, you only should use the proper URL not ant other tag. Change it to something like this:
Link

Please pay attention to the FILENAME and how to print out the filename that I choose from my computer?

it is a simple question, how to print out the selected file name, thanks.
public class CSVMAX {
public CSVRecord hottestInManyDays() {
//select many csv files from my computer
DirectoryResource dr = new DirectoryResource();
CSVRecord largestSoFar = null;
//read every row and implement the method we just define
for(File f : dr.selectedFiles()) {
FileResource fr = new FileResource(f);
CSVRecord currentRow = hottestHourInFile(fr.getCSVParser());
if (largestSoFar == null) {
largestSoFar = currentRow;
}
else {
double currentTemp = Double.parseDouble(currentRow.get("TemperatureF"));
double largestTemp = Double.parseDouble(largestSoFar.get("TemperatureF"));
//Check if currentRow’s temperature > largestSoFar’s
if (currentTemp > largestTemp) {
//If so update largestSoFar to currentRow
largestSoFar = currentRow;
}
}
}
return largestSoFar;
}
here I want to print out the file name but I dont know how to do that.
public void testHottestInManyDay () {
CSVRecord largest = hottestInManyDays();
System.out.println("hottest temperature on that day was in file " + ***FILENAME*** + largest.get("TemperatureF") +
" at " + largest.get("TimeEST"));
}
}
Ultimately, it seems that hottestInManyDays() will need to return this information.
Does CSVRecord have a property for that?
Something like this:
CSVRecord currentRow = hottestHourInFile(fr.getCSVParser());
currentRow.setFileName(f.getName());
If not, can such a property be added to it?
Maybe CSVRecord doesn't have that property. But it can be added?:
private String _fileName;
public void setFileName(String fileName) {
this._fileName = fileName;
}
public String getFileName() {
return this._fileName;
}
If not, can you create a wrapper class for both pieces of information?
If you can't modify CSVRecord and it doesn't have a place for the information you want, wrap it in a class which does. Something as simple as this:
class CSVWrapper {
private CSVRecord _csvRecord;
private String _fileName;
// getters and setters for the above
// maybe also a constructor? make them final? your call
}
Then return that from hottestInManyDays() instead of a CSVRecord. Something like this:
CSVWrapper csvWrapper = new csvWrapper();
csvWrapper.setCSVRecord(currentRow);
csvWrapper.setFileName(f.getName());
Changing the method signature and return value as needed, of course.
However you do it, once it's on the return value from hottestInManyDays() you can use it in the method which consumes that:
CSVWrapper largest = hottestInManyDays();
System.out.println("hottest temperature on that day was in file " + largest.getFileName() + largest.getCSVRecord().get("TemperatureF") +
" at " + largest.getCSVRecord().get("TimeEST"));
(Note: If the bits at the very end there don't sit right as a Law Of Demeter violation, then feel free to extend the wrapper to include pass-thru operations as needed. Maybe even have it share a common interface with CSVRecord so it can be used as a drop-in replacement for one as needed elsewhere in the system.)
Referring to the line for(File f : dr.selectedFiles())
f is a [File]. It has a toString() method [from docs],
Returns the pathname string of this abstract pathname. This is just
the string returned by the getPath() method.
So, in the first line inside the loop, you can put System.out.println(f.toString()); to print out the file path.
Hope this helps clear a part of the story.
Now, to update this string, I see you are using some object that is called largest in testHottestInManyDay(). You should add a filepath string in this object and set it inside the else block.
One has to return both the CSVRecord and the File. Either in a newly made class.
As CSVRecord can be converted to a map, add the file name to the map, using a new column name, here "FILENAME."
public Map<String, String> hottestInManyDays() {
//select many csv files from my computer
DirectoryResource dr = new DirectoryResource();
CSVRecord largestSoFar = null;
File fileOfLargestSoFar = null;
//read every row and implement the method we just define
for (File f : dr.selectedFiles()) {
FileResource fr = new FileResource(f);
CSVRecord currentRow = hottestHourInFile(fr.getCSVParser());
if (largestSoFar == null) {
largestSoFar = currentRow;
fileOfLargestSoFar = f;
}
else {
double currentTemp = Double.parseDouble(currentRow.get("TemperatureF"));
double largestTemp = Double.parseDouble(largestSoFar.get("TemperatureF"));
//Check if currentRow’s temperature > largestSoFar’s
if (currentTemp > largestTemp) {
//If so update largestSoFar to currentRow
largestSoFar = currentRow;
fileOfLargestSoFar = f;
}
}
}
Map<String, String> map = new HashMap<>(largestSoFar.toMap());
map.put("FILENAME", fileOfLargestSoFar.getPath());
return map;
}
Map<String, String> largest = hottestInManyDays();
System.out.println("hottest temperature on that day was in file "
+ largest.get("FILENAME") + largest.get("TemperatureF") +
" at " + largest.get("TimeEST"));

Unable to insert data in number format in Excel

I've the below Java (Selenium) method that will insert data into Excel sheet:
private static void getPaceNumber(WebDriver chromeDriver, String dBName, XSSFSheet paceSheet, String pubName, int i,
XSSFCell cell, XSSFWorkbook workbook) throws Exception {
CellStyle style = workbook.createCellStyle();
cell = paceSheet.getRow(i).createCell(1);
if (dBName == "" || dBName.equals("null")) {
cell.setCellValue("N/A");
} else {
chromeDriver.findElement(By.xpath("html/body/form[2]/b/b/table/tbody/tr[2]/td[2]/textarea"))
.sendKeys("\"" + dBName + "\"");
chromeDriver.findElement(By.xpath("html/body/form[2]/b/b/table/tbody/tr[4]/td[2]/input[1]")).click();
List<WebElement> pace = chromeDriver
.findElements(By.xpath("html/body/form[2]/table[1]/tbody/tr[2]/td[2]/input[1]"));
int paceSize = pace.size();
if (paceSize >= 1) {
int dbPaceNumber = Integer.parseInt(
chromeDriver.findElement(By.xpath("html/body/form[2]/table[1]/tbody/tr[2]/td[2]/input[1]"))
.getAttribute("value"));
chromeDriver.findElement(By.xpath(".//*[#id='searchPublication']")).click();
chromeDriver.findElement(By.xpath("html/body/form[2]/b/b/table/tbody/tr[2]/td[2]/textarea"))
.sendKeys("\"" + pubName + "\"");
chromeDriver.findElement(By.xpath("html/body/form[2]/b/b/table/tbody/tr[4]/td[2]/input[1]")).click();
int pubPaceNumber = Integer.parseInt(
chromeDriver.findElement(By.xpath("html/body/form[2]/table[1]/tbody/tr[2]/td[2]/input[1]"))
.getAttribute("value"));
if (dbPaceNumber == pubPaceNumber) {
cell.setCellValue(dbPaceNumber);
} else {
cell.setCellValue(dbPaceNumber + "\n" + pubPaceNumber);
style.setWrapText(true);
cell.setCellStyle(style);
}
} else {
List<WebElement> table = chromeDriver
.findElements(By.xpath("html/body/form[2]/table[1]/tbody/tr[4]/td/b"));
int tabSize = table.size();
if (tabSize == 1) {
chromeDriver.findElement(By.xpath(".//*[#id='searchPublication']")).click();
chromeDriver.findElement(By.xpath("html/body/form[2]/b/b/table/tbody/tr[2]/td[2]/textarea"))
.sendKeys("\"" + pubName + "\"");
chromeDriver.findElement(By.xpath("html/body/form[2]/b/b/table/tbody/tr[4]/td[2]/input[1]"))
.click();
List<WebElement> paceWithFPN = chromeDriver
.findElements(By.xpath("html/body/form[2]/table[1]/tbody/tr[2]/td[2]/input[1]"));
int paceWithFPNSize = paceWithFPN.size();
if (paceWithFPNSize >= 1) {
int paceSubNumber = Integer.parseInt(chromeDriver
.findElement(By.xpath("html/body/form[2]/table[1]/tbody/tr[2]/td[2]/input[1]"))
.getAttribute("value"));
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
cell.setCellValue(paceSubNumber);
} else {
cell.setCellValue("N/A");
}
} else {
cell.setCellValue("N/A");
}
}
}
}
I want to check the value with two different criteria, if both of them are same, insert the value in Excel cell, else insert both the values in a single cell. Basically the values retrieved are of integer type. I'm able to insert the values correctly, but if there are two values, they are getting inserted as a single line (one continuation of the other). The single value is automatically aligned right in Excel cell (in general number format).
Where there are two values, I need to double click on the cell and then they are shown in two line format, and also they are displayed as strings (left aligned).
I'm aware that when I use a +"XXX"+ the resultant is a string, but how can I make this into an integer?
Like single value, this has to be right aligned and also is there a way I can get a line break automatically inside the cell?
**Current output:** **Expected Output:**
I think you have to increase the rowheight of the row that contains the cell.
In the beginning:
int defaultHeight = paceSheet.getRow(0).getHeight();
and later, when you create such a cell:
paceSheet.getRow(i).setHeight(defaultHeight*2);
Those lines:
style.setWrapText(true);
style.setAlignment(CellStyle.ALIGN_RIGHT);
are something you just have to do once (e.g.right after creation) since style is the same for all affected cells.

Categories