Let us begin with building the kernel for the document processing. This kernel is also a part, which we have used as proof of concept. In the previous post (https://guedebyte.wordpress.com/2012/09/07/documents-and-spreadsheets-i/) our focus was on the idea. This entry is about the main processing.
If you want to try this in your own project, please perform the following steps:
-
Download Eclipse Indigo
-
Download Apache POI 3.8
- Prepare a project based on the following instructions of René Winkelmeyer: http://de.slideshare.net/muenzpraeger/uklug-2012-xpages-extensibility-api-going-deep (starts at page 23)
The first step is, create a “lib” directory in your project and add the apache-poi libraries to this directory.
Add the libraries to the build-path (without the commons-log and log4j), by selecting the libraries: Left click and select “Build-Path/add to build-path”.
Commons-log and log4j are not needed because the extensible API delivers his own implementation of log4j. If you need a dedicated version of those libraries, it is recommended to encapsulate the code in a separate plugin, which exports only the functionality to the plugin, which extends the api. Both plugins have to be in the same feature (we will come back to this later).
We can now build an interface, witch represents the bookmarks.
package biz.webgate.dominoext.poi.component.data.document;
public interface IDocumentBookmark {
   public String getName();
   public String getValue();
}
Programming against an interface will definitely safe time. While I was building the prototype of this application for a proof of concept, I had to build my own implementation of IDocumentBookmark, because it was not possible to solve all dependencies of the Xpages Framework. See the difference. The first sample is my prototype implementation, the second one is my library implementation:
1. Prototype implementation
import biz.webgate.dominoext.poi.component.data.document.IDocumentBookmark;
public class BMImplementation implements IDocumentBookmark {
private String m_Name;
private String m_Value;
public BMImplementation(String name, String value) {
super();
m_Name = name;
m_Value = value;
}
@Override
public String getName() {
returnm_Name;
}
@Override
public String getValue() {
return m_Value;
}
}
2. Library implementation:
package biz.webgate.dominoext.poi.component.data.document;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import com.ibm.xsp.complex.ValueBindingObjectImpl;
public class DocumentBookmark extends ValueBindingObjectImpl implements IDocumentBookmark {
private String m_Name;
private String m_Value;
public String getName() {
if (m_Name != null) {
return m_Name;
}
ValueBinding vb = getValueBinding(“name”);
if (vb != null) {
return (String) vb.getValue(getFacesContext());
}
return null;
}
public void setName(String name) {
m_Name = name;
}
public String getValue() {
if (m_Value != null) {
return m_Value;
}
ValueBinding vb = getValueBinding(“value”);
if (vb != null) {
return (String) vb.getValue(getFacesContext());
}
return null;
}
public void setValue(String value) {
m_Value = value;
}
@Override
public void restoreState(FacesContext context, Object value) {
Object[] state = (Object[]) value;
super.restoreState(context, state[0]);
m_Name = (String) state[1];
m_Value = (String) state[2];
}
@Override
public Object saveState(FacesContext context) {
Object[] state = new Object[3];
state[0] = super.saveState(context);
state[1] = m_Name;
state[2] = m_Value;
return state;
}
}
Now let’s build the kernel. We do a simple 3 step approach.
-
Building a XWPFDocument (from a InputStream)
-
Replacing all Bookmarks in the document
-
Writing the XWPFDocument to a ByteArrayBuffer
Here is the code to build a XWPFDocument
public XWPFDocument getDocument(InputStream inDocument) {
try {
XWPFDocument dxReturn = new XWPFDocument(inDocument);
return dxReturn;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
The bookmark replacement is little bit harder. Let’s begin with the main entry:
publicint processBookmarks2Document(XWPFDocument dxProcess, List<IDocumentBookmark> arrBookmarks) {
// First Prozessing all paragraphs.
for (XWPFParagraph paraCurrent : dxProcess.getParagraphs()) {
processBookmarks2Paragraph(arrBookmarks, paraCurrent);
}
// All Tables
for (XWPFTable tabCurrent : dxProcess.getTables()) {
processBookmarks2Table(arrBookmarks, tabCurrent);
}
// All Headers
for (XWPFHeader headCurrent : dxProcess.getHeaderList()) {
for (XWPFParagraph paraCurrent : headCurrent.getParagraphs()) {
processBookmarks2Paragraph(arrBookmarks, paraCurrent);
}
for (XWPFTable tabCurrent : headCurrent.getTables()) {
processBookmarks2Table(arrBookmarks, tabCurrent);
}
}
// All Footers
for (XWPFFooter footCurrent : dxProcess.getFooterList()) {
for (XWPFParagraph paraCurrent : footCurrent.getParagraphs()) {
processBookmarks2Paragraph(arrBookmarks, paraCurrent);
}
for (XWPFTable tabCurrent : footCurrent.getTables()) {
processBookmarks2Table(arrBookmarks, tabCurrent);
}
}
return 1;
}
First we process all paragraphs in the document, followed by all tables. Then we do the same with the headers and footers. They do also contains tables and paragraphs. So let’s see how we process the paragraphs:
private void processBookmarks2Paragraph( List<IDocumentBookmark> arrBookmarks, XWPFParagraph paraCurrent) {
for (XWPFRun runCurrent : paraCurrent.getRuns()) {
processBookmarks2Run(runCurrent, arrBookmarks);
}
}
Paragraphs contain “Run” elements, which contain a text, that has the same styling and formatting. These run elements are processed in the following function:
public int processBookmarks2Run(XWPFRun runCurrent, List<IDocumentBookmark> arrBookmarks) {
String strText = runCurrent.getText(0);
if (strText != null) {
for (IDocumentBookmark bmCurrent : arrBookmarks) {
String strValue = bmCurrent.getValue();
strValue = strValue == null ? “” : strValue;
if (bmCurrent.getName() != null) {
strText = strText.replaceAll(“<<“ + bmCurrent.getName()+ “>>”, strValue);
}
}
}
runCurrent.setText(strText, 0);
return 1;
}
Our tables also contain paragraphs and run elements. We will also reuse this method. Tables are built on rows and cells, see how we browse that:
private void processBookmarks2Table(List<IDocumentBookmark> arrBookmarks, XWPFTable tabCurrent) {
for (XWPFTableRow tabRow : tabCurrent.getRows()) {
for (XWPFTableCell tabCell : tabRow.getTableCells()) {
for (XWPFParagraph paraCurrent : tabCell.getParagraphs()) {
processBookmarks2Paragraph(arrBookmarks, paraCurrent);
}
}
}
}
That’s all you need for the kernel. The full class will be available with the source code later this year. The next step is to build the UI for the domino designer. Watch out for the next episode.