All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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