-
Notifications
You must be signed in to change notification settings - Fork 6
Tutorial
This tutorial is extracted from https://github.com/jferard/fastods/tree/master/fastods-examples/src/main/java/com/github/jferard/fastods/examples. See the classes in this directory for the full code.
Writing a full documentation would be a considerable work, because every time you change the library, you have to rewrite the doc.
My idea is to provide a set of examples of the features of FastODS.
Before you start, add the following dependency to your POM (Maven users):
<dependency>
<groupId>com.github.jferard</groupId>
<artifactId>fastods</artifactId>
<version>0.6.1</version>
</dependency>
For other build tools, look at: https://search.maven.org/artifact/com.github.jferard/fastods/0.6.1/jar
Let's start with the famous "Hello, World!" example.
As stated in the javadoc, "An OdsFactory is the entry point for creating ODS documents." Every time you want to create an ODS document, you'll start with something like that:
final OdsFactory odsFactory = OdsFactory.create(Logger.getLogger("hello-world"), Locale.US);
Now, you can create an ODS writer. You have the choice: either you give the filename now, or you keep it for the end. Let's create an anonymous writer: the content will live in memory until we save it to a file:
final AnonymousOdsFileWriter writer = odsFactory.createWriter();
The writer owns a document that we need to create a spreadsheet:
final OdsDocument document = writer.document();
Okay, let's go. We create a new table (a new sheet indeed) named "hello-world":
final Table table = document.addTable("hello-world");
We get the first row:
final TableRow row = table.getRow(0);
And the first cell of the first row:
final TableCell cell = row.getOrCreateCell(0);
Note that we could have chained the calls:
TableCell cell = document.addTable("hello-world").getRow(0).getOrCreateCell(0)
Finally, we put the famous sentence in this cell A1
cell.setStringValue("Hello, world!");
And save the file.
writer.saveAs(new File("generated_files", "a_hello_world_example.ods"));
With a mvn clean verify
at the root of the project, you can check the result in the
fastods-examples/generated-files
directory.
Now, we want to write values in other cells that the A1 cell.
We start with the (now) usual boilerplate code to get a document:
final OdsFactory odsFactory = OdsFactory.create(Logger.getLogger("accessing"), Locale.US);
final AnonymousOdsFileWriter writer = odsFactory.createWriter();
final OdsDocument document = writer.document();
You know how to create a table:
final Table table1 = document.addTable("direct-access");
Get the first row (rows and columns start at 0):
TableRow row = table1.getRow(0);
The first cell of the first row:
TableCell cell = row.getOrCreateCell(0);
And set a value.
cell.setStringValue("A1");
Note that the access is a write only access. You can't read the content of a cell. You can have direct access to any cell, but it has a cost. If the row doesn't exist yet in the list of rows, it will create all rows between the last row and the actual row.
row = table1.getRow(6);
You can have direct access to cells
cell = row.getOrCreateCell(1);
cell.setStringValue("B7");
With a TableHelper, direct access might be easier to write...
final TableHelper tableHelper = TableHelper.create();
...but note that the cell is referred by row then column (as matrices in maths). To access the cell "F2", you'll use:
cell = tableHelper.getCell(table1, 1, 5);
cell.setStringValue("F2");
You can use an address, but there is the cost of parsing that address and a risk of malformed address:
try {
cell = tableHelper.getCell(table1, "E3");
cell.setStringValue("E3");
} catch (final ParseException e) {
/* this won't happen here! */
}
To be (almost) complete, there is another way to write a value to a cell:
try {
tableHelper.setCellValue(table1, "D4", CellValue.fromObject("D4"));
} catch (final ParseException e) {
/* this won't happen here! */
}
Direct access may be useful, but FastODS was designed for a relative access Create a new table:
final Table table2 = document.addTable("relative-access");
We want ten rows of data
for (int r = 0; r < 10; r++) {
The Table object has an internal row index (that is updated by the getRow
method).
Just call nextRow
to make the index advance by one (you have to call nextRow
before you write data, to get the first row):
final TableRow tableRow = table2.nextRow();
And then create a "walker" for this row:
final TableCellWalker cellWalker = tableRow.getWalker();
Now, we want nine columns for each row:
for (int c = 0; c < 9; c++) {
Add the value to each cell
cellWalker.setStringValue(String.valueOf((char) (c + 'A')) + String.valueOf(r + 1));
And then push one cell right.
cellWalker.next();
}
}
And save the file.
writer.saveAs(new File("generated_files", "b_accessing_example.ods"));
Note: There is a slight inconsistency between table.newtRow
(before using
the row) and cellWalker.next
(after using the cell). Maybe I'll fix it before
version 1.0...
Four elements define the content of a cell:
- the value
- the type (string, float, boolean, ...)
- the style (font, background color, border, ...)
- the format, or data style (number of digits for a float, date format for a date, ...)
So far, we just created cells of type string, with neither style nor data style. Now, we will create cells with a more varied content.
Now, we will create cells of different types. First, a table:
final Table table = document.addTable("types");
We add a header:
TableRow tableRow = table.nextRow();
TableCellWalker cellWalker = tableRow.getWalker();
cellWalker.setStringValue("Type");
cellWalker.next();
cellWalker.setStringValue("Example");
The first row contains a boolean:
tableRow = table.nextRow();
cellWalker = tableRow.getWalker();
cellWalker.setStringValue("Boolean");
cellWalker.next();
cellWalker.setBooleanValue(true);
The second row contains a currency:
tableRow = table.nextRow();
cellWalker = tableRow.getWalker();
cellWalker.setStringValue("Currency");
cellWalker.next();
cellWalker.setCurrencyValue(10.5, "USD");
The third row contains a date:
tableRow = table.nextRow();
cellWalker = tableRow.getWalker();
cellWalker.setStringValue("Date");
cellWalker.next();
cellWalker.setDateValue(new GregorianCalendar(2014, 9, 17, 9, 0, 0));
The fourth row contains a float:
tableRow = table.nextRow();
cellWalker = tableRow.getWalker();
cellWalker.setStringValue("Float");
cellWalker.next();
cellWalker.setFloatValue(3.14159);
The fifth row contains a percentage:
tableRow = table.nextRow();
cellWalker = tableRow.getWalker();
cellWalker.setStringValue("Percentage");
cellWalker.next();
cellWalker.setPercentageValue(0.545);
The sixth row contains...
tableRow = table.nextRow();
cellWalker = tableRow.getWalker();
cellWalker.setStringValue("String");
cellWalker.next();
cellWalker.setStringValue("A String");
The seventh row contains a time (that mean a duration):
tableRow = table.nextRow();
cellWalker = tableRow.getWalker();
cellWalker.setStringValue("Time");
cellWalker.next();
cellWalker.setTimeValue(3600);
The eighth row contains nothing
tableRow = table.nextRow();
cellWalker = tableRow.getWalker();
cellWalker.setStringValue("Void");
cellWalker.next();
cellWalker.setVoidValue();
FastODS can guess types, based on Java object types. It's useful when we try to auto
import typed data, e.g. from a ResultSet
. We can use this ability to reduce the
boilerplate code.
Let's define two lists:
final List<String> A = Arrays
.asList("Type", "Boolean", "Currency", "Date", "Float", "Percentage", "String",
"Void");
final List<Object> B = Arrays
.<Object>asList("Type guess example", true, new CurrencyValue(10.5f, "USD")
, new GregorianCalendar(2014, 9, 17, 9, 0, 0),
3.14159, new PercentageValue(0.545f), "A String", null);
As you can see, some types are not guessable: is 0.545
a float or a percentage? For
FastODS, it is a float. What Java type will map a currency value? We have to use specific
types...
We skip a row for readability:
table.nextRow();
Now, we can use setValue
to take advantage of the type guess:
for (int r=0; r<A.size(); r++) {
tableRow = table.nextRow();
cellWalker = tableRow.getWalker();
cellWalker.setStringValue(A.get(r));
cellWalker.next();
cellWalker.setCellValue(CellValue.fromObject(B.get(r)));
}