package com.horstmann.violet.framework.file.persistence;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import javax.imageio.ImageIO;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import org.apache.commons.codec.binary.Base64OutputStream;
import com.horstmann.violet.framework.file.export.FileExportService;
import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean;
import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean;
import com.horstmann.violet.framework.util.VersionChecker;
import com.horstmann.violet.product.diagram.abstracts.IGraph;
@ManagedBean(registeredManually=true)
public class XHTMLPersistenceService implements IFilePersistenceService
{
private static final String TEMPLATE_FILE = "XHTMLFileTemplate.violet.html";
private static final String IMAGE_TYPE = "png";
private static final String HTML_INLINE_IMAGE_PREFIX = "data:image/png;base64,";
private static final String TEMPLATE_VERSION_KEY = "${version}";
private static final String TEMPLATE_IMAGE_KEY = "${image}";
private static final String TEMPLATE_XMLCONTENT_KEY = "${content}";
private XStreamBasedPersistenceService xstreamService = new XStreamBasedPersistenceService();
@InjectedBean
private VersionChecker versionChecker;
@Override
public void write(IGraph graph, OutputStream out)
{
try
{
OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
InputStream templateAsStream = this.getClass().getResourceAsStream(TEMPLATE_FILE);
String template = getInputStreamContent(templateAsStream);
ByteArrayOutputStream graphOutputStream = new ByteArrayOutputStream();
xstreamService.write(graph, graphOutputStream);
String graphString = graphOutputStream.toString();
ByteArrayOutputStream imageOutputStream = new ByteArrayOutputStream();
Base64OutputStream base64ImageOutputStream = new Base64OutputStream(imageOutputStream);
ImageIO.write(FileExportService.getImage(graph), IMAGE_TYPE, base64ImageOutputStream);
String imageString = HTML_INLINE_IMAGE_PREFIX + imageOutputStream.toString();
template = template.replace(TEMPLATE_VERSION_KEY, this.versionChecker.getAppVersionNumber());
template = template.replace(TEMPLATE_XMLCONTENT_KEY, graphString);
template = template.replace(TEMPLATE_IMAGE_KEY, imageString);
writer.write(new String(template.getBytes()));
templateAsStream.close();
imageOutputStream.close();
base64ImageOutputStream.close();
writer.close();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
public IGraph read(InputStream in) throws IOException
{
InputStreamReader reader = new InputStreamReader(in, "UTF-8");
XHTMLPersistenceServiceParserGetter kit = new XHTMLPersistenceServiceParserGetter();
HTMLEditorKit.Parser parser = kit.getParser();
StringWriter writer = new StringWriter();
HTMLEditorKit.ParserCallback callback = new XHTMLPersistenceServiceParserCallback(writer);
parser.parse(reader, callback, true);
String xmlContent = writer.toString();
InputStream xmlContentStream = new ByteArrayInputStream(xmlContent.getBytes());
IGraph graph = this.xstreamService.read(xmlContentStream);
reader.close();
xmlContentStream.close();
reader.close();
writer.close();
return graph;
}
private String getInputStreamContent(InputStream in) throws IOException
{
BufferedInputStream bis = new BufferedInputStream(in);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
int result = bis.read();
while (result != -1)
{
byte b = (byte) result;
buf.write(b);
result = bis.read();
}
String content = buf.toString("UTF-8");
bis.close();
buf.close();
in.close();
return content;
}
private class XHTMLPersistenceServiceParserGetter extends HTMLEditorKit
{
public HTMLEditorKit.Parser getParser()
{
return super.getParser();
}
}
private class XHTMLPersistenceServiceParserCallback extends HTMLEditorKit.ParserCallback
{
private Writer out;
private boolean inHeader = false;
public XHTMLPersistenceServiceParserCallback(Writer out)
{
this.out = out;
}
public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes, int position)
{
if (!tag.equals(HTML.Tag.SCRIPT))
{
return;
}
if (!attributes.containsAttribute(HTML.getAttributeKey("id"), "content"))
{
return;
}
this.inHeader = true;
}
public void handleEndTag(HTML.Tag tag, int position)
{
if (tag.equals(HTML.Tag.SCRIPT))
{
if (this.inHeader)
{
this.inHeader = false;
}
}
// work around bug in the parser that fails to call flush
if (tag.equals(HTML.Tag.HTML)) this.flush();
}
@Override
public void handleComment(char[] text, int position)
{
if (this.inHeader)
{
try
{
String xmlContent = new String(text);
xmlContent = xmlContent.replace("<![CDATA[", "");
xmlContent = xmlContent.replace("]]>", "");
out.write(xmlContent);
out.flush();
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
}
}
public void flush()
{
try
{
out.flush();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
}