net.java.textilej.parser.builder.DocBookDocumentBuilder Maven / Gradle / Ivy
The newest version!
package net.java.textilej.parser.builder;
import java.io.Writer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import net.java.textilej.parser.Attributes;
import net.java.textilej.util.FormattingXMLStreamWriter;
import net.java.textilej.util.XmlStreamWriter;
public class DocBookDocumentBuilder extends AbstractXmlDocumentBuilder {
private static final Pattern CSS_CLASS_INLINE = Pattern.compile("(^|\\s+)inline(\\s+|$)");
private static Set entityReferenceToUnicode = new HashSet();
static {
entityReferenceToUnicode.add(215);
entityReferenceToUnicode.add(8211);
entityReferenceToUnicode.add(8212);
entityReferenceToUnicode.add(8220);
entityReferenceToUnicode.add(8221);
entityReferenceToUnicode.add(8216);
entityReferenceToUnicode.add(8217);
}
private String bookTitle;
private String doctype = "";
private Map acronyms = new HashMap();
private int headingLevel = 0;
private Stack blockDescriptions = new Stack();
private boolean automaticGlossary = true;
public DocBookDocumentBuilder(Writer out) {
super(out);
}
public DocBookDocumentBuilder(XmlStreamWriter writer) {
super(writer);
}
protected XmlStreamWriter createFormattingXmlStreamWriter(Writer out) {
XmlStreamWriter writer = super.createXmlStreamWriter(out);
return new FormattingXMLStreamWriter(writer) {
@Override
protected boolean preserveWhitespace(String elementName) {
return elementName.equals("code") || elementName.startsWith("literal");
}
};
}
public String getDoctype() {
return doctype;
}
public void setDoctype(String doctype) {
this.doctype = doctype;
}
public String getBookTitle() {
return bookTitle;
}
public void setBookTitle(String bookTitle) {
this.bookTitle = bookTitle;
}
@Override
public void acronym(String text, String definition) {
String previousDef = acronyms.put(text, definition);
if (previousDef != null && previousDef.length() > definition.length()) {
acronyms.put(text,previousDef);
}
writer.writeStartElement("glossterm");
characters(text);
writer.writeEndElement();
}
@Override
public void link(Attributes attributes,String href,final String text) {
link(attributes,href,new ContentEmitter() {
public void emit() {
writer.writeCharacters(text);
}
});
}
private void link(Attributes attributes,String href, ContentEmitter emitter) {
ensureBlockElementsOpen();
if (href.startsWith("#")) {
if (href.length() > 1) {
writer.writeStartElement("link");
writer.writeAttribute("linkend", href.substring(1));
emitter.emit();
writer.writeEndElement(); // link
} else {
emitter.emit();
}
} else {
writer.writeStartElement("ulink");
writer.writeAttribute("url", href);
emitter.emit();
writer.writeEndElement(); // ulink
}
}
private interface ContentEmitter {
public void emit();
}
@Override
public void beginBlock(BlockType type, Attributes attributes) {
if (headingLevel == 0) {
beginHeading(1,new Attributes());
endHeading();
}
String elementName;
String[] elementNames = null;
boolean allowTitle = false;
boolean closeElementsOnBlockStart = false;
BlockDescription previousBlock = null;
if (!blockDescriptions.isEmpty()) {
previousBlock = blockDescriptions.peek();
}
switch (type) {
case BULLETED_LIST:
elementName = "itemizedlist";
break;
case NUMERIC_LIST:
elementName = "orderedlist";
break;
case DEFINITION_LIST:
elementName = "variablelist";
// variablelist
// varlistentry+
// term+
// listitem
//
break;
case DEFINITION_TERM:
BlockDescription blockDescription = findBlockDescription(BlockType.DEFINITION_LIST);
if (blockDescription.entrySize > 0) {
endBlockEntry(blockDescription);
}
openBlockEntry(blockDescription, new String[] { "varlistentry" });
elementName = "term";
break;
case DEFINITION_ITEM:
elementName = "listitem";
elementNames = new String[] { "para" };
closeElementsOnBlockStart = true;
break;
case FOOTNOTE:
case PARAGRAPH:
elementName = "para";
break;
case CODE:
elementName = "code";
break;
case PREFORMATTED:
elementName = "literallayout";
break;
case QUOTE:
elementName = "blockquote";
break;
case LIST_ITEM:
elementName = "listitem";
elementNames = new String[] { "para" };
closeElementsOnBlockStart = true;
break;
case TABLE:
elementName = "informaltable";
break;
case TABLE_CELL_HEADER:
elementName = "th";
break;
case TABLE_CELL_NORMAL:
elementName = "td";
break;
case TABLE_ROW:
elementName = "tr";
break;
case INFORMATION:
elementName = "important";
allowTitle = true;
break;
case NOTE:
elementName = "note";
allowTitle = true;
break;
case WARNING:
elementName = "warning";
allowTitle = true;
break;
case TIP:
elementName = "tip";
allowTitle = true;
break;
case PANEL:
elementName = "note"; // docbook has nothing better for 'note'
allowTitle = true;
break;
default:
throw new IllegalStateException(type.name());
}
if (previousBlock != null && previousBlock.closeElementsOnBlockStart) {
endBlockEntry(previousBlock);
}
int blockSize = 1;
writer.writeStartElement(elementName);
applyAttributes(attributes);
if (elementNames != null) {
for (String name: elementNames) {
writer.writeStartElement(name);
}
}
if (allowTitle && attributes.getTitle() != null) {
writer.writeStartElement("title");
writer.writeCharacters(attributes.getTitle());
writer.writeEndElement();
}
blockDescriptions.push(new BlockDescription(type,blockSize,elementNames,closeElementsOnBlockStart));
}
@Override
public void endBlock() {
final BlockDescription blockDescription = blockDescriptions.pop();
int size = blockDescription.size+blockDescription.entrySize;
for (int x = 0;x toLevel) {
writer.writeEndElement();
--headingLevel;
}
}
private void writeGlossaryAppendix() {
if (!acronyms.isEmpty() && automaticGlossary) {
writer.writeStartElement("appendix");
writer.writeAttribute("id", "glossary");
writer.writeStartElement("title");
writer.writeAttribute("id", "glossary-end");
writer.writeCharacters("Glossary");
writer.writeEndElement(); // title
writer.writeStartElement("glosslist");
for (Map.Entry glossEntry : new TreeMap(acronyms).entrySet()) {
writer.writeStartElement("glossentry");
writer.writeStartElement("glossterm");
writer.writeCharacters(glossEntry.getKey());
writer.writeEndElement(); // glossterm
writer.writeStartElement("glossdef");
writer.writeStartElement("para");
writer.writeCharacters(glossEntry.getValue());
writer.writeEndElement(); // para
writer.writeEndElement(); // glossdef
writer.writeEndElement(); // glossentry
}
writer.writeEndElement(); // glosslist
writer.writeEndElement(); // appendix
}
}
@Override
public void endSpan() {
writer.writeEndElement();
}
@Override
public void characters(String text) {
ensureBlockElementsOpen();
super.characters(text);
}
@Override
public void charactersUnescaped(String literal) {
ensureBlockElementsOpen();
// note: this *may* have HTML tags in it
writer.writeLiteral(literal);
// Logger.getLogger(DocBookDocumentBuilder.class.getName()).warning("HTML literal not supported in DocBook");
}
private void ensureBlockElementsOpen() {
if (!blockDescriptions.isEmpty()) {
BlockDescription blockDescription = blockDescriptions.peek();
if (blockDescription.entrySize == 0 && blockDescription.nestedElementNames != null) {
openBlockEntry(blockDescription, blockDescription.nestedElementNames);
}
}
}
@Override
public void entityReference(String entity) {
ensureBlockElementsOpen();
if (entity.startsWith("#")) {
String numeric = entity.substring(1);
int base = 10;
if (numeric.startsWith("x")) {
numeric = entity.substring(1);
base = 16;
}
int unicodeValue = Integer.parseInt(numeric,base);
if (entityReferenceToUnicode.contains(unicodeValue)) {
writer.writeCharacters(""+((char)unicodeValue));
return;
}
}
writer.writeEntityRef(entity);
}
@Override
public void image(Attributes attributes, String url) {
ensureBlockElementsOpen();
String cssClass = attributes.getCssClass();
boolean inlined = false;
if (cssClass != null && CSS_CLASS_INLINE.matcher(cssClass).find()) {
inlined = true;
}
emitImage(attributes, url,inlined);
}
private void emitImage(Attributes attributes, String url,boolean inline) {
ensureBlockElementsOpen();
writer.writeStartElement(inline?"inlinemediaobject":"mediaobject");
applyAttributes(attributes);
writer.writeStartElement("imageobject");
writer.writeEmptyElement("imagedata");
writer.writeAttribute("fileref", makeUrlAbsolute(url));
writer.writeEndElement(); // imageobject
writer.writeEndElement(); // inlinemediaobject or mediaobject
}
@Override
public void imageLink(Attributes linkAttributes, final Attributes imageAttributes,String href, final String imageUrl) {
link(linkAttributes,href,new ContentEmitter() {
public void emit() {
emitImage(imageAttributes,imageUrl,true);
}
});
}
@Override
public void lineBreak() {
ensureBlockElementsOpen();
// no equivalent in DocBook.
characters("\n");
}
private BlockDescription findBlockDescription(BlockType type) {
for (int x = blockDescriptions.size()-1;x>=0;--x) {
BlockDescription blockDescription = blockDescriptions.get(x);
if (blockDescription.type == type) {
return blockDescription;
}
}
return null;
}
private static class BlockDescription {
BlockType type;
int size;
int entrySize; // the size of an entry, if it is open, otherwise 0
final String[] nestedElementNames;
final boolean closeElementsOnBlockStart;
public BlockDescription(BlockType type,int size, String[] nestedElementNames, boolean closeElementsOnBlockStart) {
this.size = size;
this.entrySize = nestedElementNames==null?0:nestedElementNames.length;
this.type = type;
this.nestedElementNames = nestedElementNames;
this.closeElementsOnBlockStart = closeElementsOnBlockStart;
}
}
/**
* Indicate if this builder should generate an automatic glossary if acronyms are used.
* When the automatic glossary is enabled and acronyms are used in the document, then an
* appendix
with title 'Glossary' is added to the document, with a glosslist
* generated for all of the acronyms that appear in the document.
*
* The default is true.
*/
public boolean isAutomaticGlossary() {
return automaticGlossary;
}
/**
* Indicate if this builder should generate an automatic glossary if acronyms are used.
* When the automatic glossary is enabled and acronyms are used in the document, then an
* appendix
with title 'Glossary' is added to the document, with a glosslist
* generated for all of the acronyms that appear in the document.
*
* The default is true.
*/
public void setAutomaticGlossary(boolean automaticGlossary) {
this.automaticGlossary = automaticGlossary;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy