package net.sourceforge.rtf.handler;
import java.io.IOException;
import java.util.Stack;
import net.sourceforge.rtf.IRTFDocumentParser;
import net.sourceforge.rtf.document.RTFAnnotation;
import net.sourceforge.rtf.document.RTFDocument;
import net.sourceforge.rtf.document.RTFElement;
import net.sourceforge.rtf.document.RTFEndBookmark;
import net.sourceforge.rtf.document.RTFField;
import net.sourceforge.rtf.document.RTFPage;
import net.sourceforge.rtf.document.RTFRow;
import net.sourceforge.rtf.document.RTFStartBookmark;
import net.sourceforge.rtf.document.RTFUserProperty;
import net.sourceforge.rtf.parser.AbstractDefaultRTFParser;
/**
* RTF handler which implement AbstractDefaultRTFParser to create RTFDocument
* element RTF of RTF stream source.
*
* @see net.sourceforge.rtf.document.RTFDocument
* @version 1.0.0
* @author <a href="mailto:angelo.zerr@gmail.com">Angelo ZERR</a>
*/
public class RTFDocumentHandler extends AbstractDefaultRTFParser implements
IRTFDocumentParser {
private RTFDocument document = null; // Document RTF
private RTFElement currentRTFElement = null; // current RTF element
// parsed
private int currentLevel; // level of current group
// private RTFRow lastRTFRow = null; // last RTF row parsed
// private int levelOfLastField = -1; // level of last RTF field
// private RTFBookmark lastRTFBookmark = null; // last RTF bookmark parsed
// private RTFBookmark lastRTFUserProperty = null; // last RTF user property
// parsed
private RTFElement lastRTFElement = null;
// private RTFField lastRTFField = null;
private Stack lastRTFFields = new Stack();
private Stack levelOfLastFields = new Stack();
// private RTFBookmark lastRTFBookmark = null;
private String startGroup = null; // start group
private int levelOfAnnotation = -1;
/**
* Implement startGroup event to create RTF root element RTFDocument on
* first level and store start group character.
*
* @param startGroupCharacter
* start group character <b>{</b>.
* @param level
* of current group.
* @throws IOException
*/
protected void startGroup(char startGroupCharacter, int level)
throws IOException {
this.currentLevel = level; // store level of current group
if (level == 1) {
// start parsing of RTF source
// Create RTFDocument object which is root of RTF source.
document = new RTFDocument();
currentRTFElement = document;
getCurrentRTFElement().addRTFString(
String.valueOf(startGroupCharacter));
} else {
// store start group character '{'.
if (startGroup == null)
startGroup = String.valueOf(startGroupCharacter);
else {
// This case sometimes (according to MS Word version
// comes from when there is several start group. Ex :
/*
* {{\field{\*\fldinst SYMBOL 165 \\f "Wingdings" \\s
* 12}{\fldrslt\f14\fs24}}}
*/
startGroup += String.valueOf(startGroupCharacter);
}
}
}
/**
* Implement endGroup event to add end group character to last row, last
* bookmark... parsed otherwise into current RTF element.
*
* @param endGroupCharacter
* end group character <b>}</b>.
* @param level
* of current group.
* @throws IOException
*/
protected void endGroup(char endGroupCharacter, int level)
throws IOException {
this.currentLevel = level; // store level of current group
if (startGroup != null) {
// the last start group character has been not added
// into current RTF element. Add it.
getCurrentRTFElement().addRTFString(startGroup);
startGroup = null;
}
if (lastRTFElement != null) {
// the last RTF element (row, bookmarok, userProperty...) parsed is
// not end by } character
// add character into the RTF element
lastRTFElement.addRTFString(String.valueOf(endGroupCharacter));
lastRTFElement = null;
return;
}
// Add } character into current RTF element
getCurrentRTFElement().addRTFString(String.valueOf(endGroupCharacter));
int size = levelOfLastFields.size();
if (size > 0) {
Integer levelOfLastField = (Integer) levelOfLastFields
.get(size - 1);
if (levelOfLastField.intValue() == level) {
// The current level equal to
// level of the last RTF field parsed.
// RTF field element must be ended
endField();
return;
}
}
if (levelOfAnnotation == this.currentLevel) {
endAnnotation();
}
}
/**
* Implement startRow to start and add RTF row element into current RTF
* element parsed.
*
* @param content
* RTF start row keyword <b>\trowd</b>.
* @throws IOException
*/
protected void startRow(String content) throws IOException {
RTFRow row = null;
boolean isNewRow = true;
if (currentRTFElement instanceof RTFRow) {
// current RTF element parsed id ROW
// (Sometimes there is Row into Row. Why???)
// new RTFRow object must not instanciate
row = (RTFRow) currentRTFElement;
isNewRow = false;
} else {
// current RTF element parsed id ROW
// instanciate new RTFRow and add it to current element
row = new RTFRow();
}
if (startGroup != null) {
// Last character parsed is start group '{'
// add it to RTF Row element
row.addRTFString(startGroup);
startGroup = null;
}
// Add content of row to RTF row
row.addRTFString(content);
if (isNewRow)
getCurrentRTFElement().addRTFElement(row);
// RTF ROW built is the current element.
currentRTFElement = row;
}
/**
* Implement endRow to end RTF row element into current RTF element parsed.
*
* @param content
* RTF end row keyword <b>\row</b>.
* @throws IOException
*/
protected void endRow(String content) throws IOException {
if (startGroup != null) {
// Last character parsed is start group '{'
// add it to RTF Row element
getCurrentRTFElement().addRTFString(startGroup);
startGroup = null;
}
// store last Row parsed
lastRTFElement = getCurrentRTFElement();
// Add RTF content into RTF row
// current RTF element begins the parent
// of RTF row
getCurrentRTFElement().addRTFString(content);
currentRTFElement = currentRTFElement.getRtfElementParent();
}
/**
* Implement inTable - if currentElement is not row, then put it and the
* preceeding text from the last element to a new row, and set it as current
* element.
*
* @param content
* RTF in table keyword <b>\intbl</b>
* @throws IOException
*/
protected void inTable(String content) throws IOException {
// if it is not already in a row, put it in one.
if (currentRTFElement instanceof RTFDocument) {
RTFRow row = new RTFRow();
// not checking startgroup, cause it's not necessary
row.addRTFString(getCurrentRTFElement().removeCurrentRTFString()
.toString());
getCurrentRTFElement().addRTFElement(row);
currentRTFElement = row;
}
currentRTFElement.addRTFString(content);
}
/**
* Implement startField to start and add RTF field element into current RTF
* element parsed.
*
* @param content
* RTF start field keyword <b>\field</b>.
* @throws IOException
*/
protected void startField(String content) throws IOException {
RTFField field = new RTFField();
if (startGroup != null) {
// Last character parsed is start group '{'
// add it to RTF Row element
field.addRTFString(startGroup);
startGroup = null;
}
field.addRTFString(content);
RTFElement element = getCurrentRTFElement();
if (element instanceof RTFRow) {
// Current element is Row
// Add Field to Row
RTFRow rtfRow = (RTFRow) element;
rtfRow.addRTFField(field);
} else {
// Add RTF field to current RTF element
getCurrentRTFElement().addRTFElement(field);
}
// RTF FIELD built is the current element.
currentRTFElement = field;
// Store last RTF field into stack
lastRTFFields.add(field);
// Store level of last field into stack
levelOfLastFields.add(new Integer(currentLevel));
}
/**
* End field, compute name of field and update current RTF element to parent
* of field/
*
* @throws IOException
*/
protected void endField() throws IOException {
// Get last RTF field stored into stack
RTFField field = (RTFField) lastRTFFields.pop();
// Remove level of last fields
levelOfLastFields.pop();
field.getName();
// lastRTFField = null;
if (currentRTFElement instanceof RTFField) {
// Current RTF element is RTF Field, get parent RTFElement
currentRTFElement = currentRTFElement.getRtfElementParent();
} else {
// Current RTF element is not RTFField, but RTFBookmark
// So the currentRTFElement must not change. This case
// sometimes come when you have Bookmark inserted into Field
// It's manage case like :
/*
* {\field\flddirty => START RTF field .... {\*\bkmkstart Texte2 =>
* BOOKMARK } FORMTEXT } => END RTF Field
*
* {\b\fs20\insrsid7366814 {\*\bkmkend Texte2 => BOOKMARK }\cell }
*/
}
}
/**
* Implement startBookmark to start and add RTF bookmark element into
* current RTF element parsed.
*
* @param content
* RTF start bookmark keyword <b>\bkmstart</b>.
* @throws IOException
*/
protected void startBookmark(String content) throws IOException {
RTFStartBookmark bookmark = new RTFStartBookmark();
if (startGroup != null) {
bookmark.addRTFString(startGroup);
startGroup = null;
}
bookmark.addRTFString(content);
getCurrentRTFElement().addRTFElement(bookmark);
lastRTFElement = bookmark;
}
/**
* Implement endBookmark to end RTF bookmark element
*
* @param content
* RTF end bookmark keyword <b>\bkmend</b>.
* @throws IOException
*/
protected void endBookmark(String content) throws IOException {
RTFEndBookmark bookmark = new RTFEndBookmark();
if (startGroup != null) {
bookmark.addRTFString(startGroup);
startGroup = null;
}
bookmark.addRTFString(content);
getCurrentRTFElement().addRTFElement(bookmark);
lastRTFElement = bookmark;
}
/**
* Implement startPage to start and add RTF page break element into current
* RTF element parsed.
*
* @param content
* RTF start page keyword <b>\page</b>.
* @throws IOException
*/
protected void startPage(String content) throws IOException {
RTFPage page = new RTFPage();
page.addRTFString(content);
currentRTFElement.addRTFElement(page);
}
protected void startUserProperty(String content) throws IOException {
RTFUserProperty userProperty = new RTFUserProperty();
if (startGroup != null) {
userProperty.addRTFString(startGroup);
startGroup = null;
}
userProperty.addRTFString(content);
getCurrentRTFElement().addRTFElement(userProperty);
currentRTFElement = userProperty;
}
protected void endUserProperty(String content) throws IOException {
RTFUserProperty userProperty = (RTFUserProperty) currentRTFElement;
if (startGroup != null) {
userProperty.addRTFString(startGroup);
startGroup = null;
}
userProperty.addRTFString(content);
userProperty.getName();
lastRTFElement = userProperty;
currentRTFElement = currentRTFElement.getRtfElementParent();
}
protected void startAnnotation(String content) throws IOException {
RTFAnnotation annotation = new RTFAnnotation();
if (startGroup != null) {
annotation.addRTFString(startGroup);
startGroup = null;
}
annotation.addRTFString(content);
getCurrentRTFElement().addRTFElement(annotation);
currentRTFElement = annotation;
levelOfAnnotation = this.currentLevel;
}
protected void endAnnotation() {
levelOfAnnotation = -1;
if (currentRTFElement instanceof RTFAnnotation)
currentRTFElement = currentRTFElement.getRtfElementParent();
}
/**
* Implement handleText to add RTF content (different of RTF keyword) into
* current RTF element.
*
* @param content
* RTF code.
* @throws IOException
*/
protected void handleText(String content) throws IOException {
if (startGroup != null) {
// Last character parsed is start group '{'
// add it to RTF Row element
getCurrentRTFElement().addRTFString(startGroup);
startGroup = null;
}
getCurrentRTFElement().addRTFString(content);
}
/**
* Return current RTF element parsed.
*
* @return current RTF element parsed.
* @throws IOException
* when current RTF element is null.
*/
private RTFElement getCurrentRTFElement() throws IOException {
if (currentRTFElement == null)
throw new IOException(
"Current RTF element is null. RTF stream is not valid.");
return currentRTFElement;
}
/**
* Return RTFDocument populated by RTF handler.
*
* @return
*/
public RTFDocument getRTFDocument() {
return document;
}
}