package util.xhtml;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.tidy.Tidy;
import org.xhtmlrenderer.extend.UserAgentCallback;
import org.xhtmlrenderer.resource.ImageResource;
import org.xhtmlrenderer.resource.XMLResource;
import org.xhtmlrenderer.simple.Graphics2DRenderer;
import org.xhtmlrenderer.swing.NaiveUserAgent;
import org.xml.sax.InputSource;
import util.configuration.LocalResourceLoader;
import util.files.FileUtils;
/**
* A mess of code which is responsible for generating a graphical rendering of a game
* @author Ethan
*
*/
@SuppressWarnings("serial")
public class GameStateRenderPanel extends JPanel {
private static final Dimension defaultSize = new Dimension(600,600);
public static Dimension getDefaultSize()
{
return defaultSize;
}
public static void renderImagefromGameXML(String gameXML, String XSL, boolean isLocalVisualization, BufferedImage backimage)
{
Graphics2DRenderer r = new Graphics2DRenderer();
if (isLocalVisualization) {
r.getSharedContext().setUserAgentCallback(getUAC());
}
String xhtml = getXHTMLfromGameXML(gameXML, XSL);
xhtml = xhtml.replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "");
InputSource is = new InputSource(new BufferedReader(new StringReader(xhtml)));
Document dom = XMLResource.load(is).getDocument();
r.setDocument(dom, "http://www.ggp.org/");
final Graphics2D g2 = backimage.createGraphics();
r.layout(g2, defaultSize);
r.render(g2);
}
private static String getXHTMLfromGameXML(String gameXML, String XSL) {
XSL = XSL.replace("<!DOCTYPE stylesheet [<!ENTITY ROOT \"http://games.ggp.org\">]>", "");
XSL = XSL.replace("&ROOT;", "http://games.ggp.org").trim();
IOString game = new IOString(gameXML);
IOString xslIOString = new IOString(XSL);
IOString content = new IOString("");
try {
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new StreamSource(xslIOString.getInputStream()));
transformer.transform(new StreamSource(game.getInputStream()),
new StreamResult(content.getOutputStream()));
} catch (Exception ex) {
ex.printStackTrace();
}
Tidy tidy = new Tidy();
tidy.setXHTML(true);
tidy.setShowWarnings(false);
tidy.setQuiet(true);
tidy.setDropEmptyParas(false);
IOString tidied = new IOString("");
tidy.parse(content.getInputStream(), tidied.getOutputStream());
return tidied.getString();
}
// Sharing UACs would probably help reduce resource usage,
// but I'm not sure about thread-safety of UAC (it seemed not to be).
private static UserAgentCallback getUAC() {
return getNewUAC();
}
private static UserAgentCallback getNewUAC() {
return new NaiveUserAgent() {
//TOdO:implement this with nio.
@SuppressWarnings("unchecked")
// TODO: _imageCache should be templatized properly so that warnings don't need to be suppressed
@Override
public ImageResource getImageResource(String uri)
{
ImageResource ir;
uri = resolveURI(uri);
ir = (ImageResource) _imageCache.get(uri);
//TODO: check that cached image is still valid
if (ir == null) {
InputStream is = null;
String[] chunks = uri.split("/");
String filename = chunks[chunks.length-1];
File localImg = new File("games", "images");
for(int i=chunks.length-1; i>0; i--)
if(chunks[i].equals("images"))
{
for(int j=i+1; j<chunks.length-1; j++)
localImg = new File(localImg, chunks[j]);
break;
}
localImg.mkdirs();
localImg = new File(localImg, filename);
boolean presentLocally = localImg.exists();
if(presentLocally)
{
try {
is = new FileInputStream(localImg);
} catch(Exception ex) { ex.printStackTrace(); }
}
else
{
is = resolveAndOpenStream(uri);
}
if (is != null) {
try {
BufferedImage img = ImageIO.read(is);
if(!presentLocally)
{
ImageIO.write(img, "png", localImg);
}
if (img == null) {
System.err.println("ImageIO.read() returned null");
throw new IOException("ImageIO.read() returned null");
}
ir = createImageResource(uri, img);
_imageCache.put(uri, ir);
} catch (FileNotFoundException e) {
System.err.println("Can't read image file; image at URI '" + uri + "' not found");
} catch (IOException e) {
System.err.println("Can't read image file; unexpected problem for URI '" + uri + "': " + e);
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
if (ir == null) {
ir = createImageResource(uri, null);
}
return ir;
}
};
}
//========XSL Loading code=====
// Code to pull in XSL from local stylesheets
public static String getXSLfromFile(String theGameKey) {
File templateFile = new File(new File(new File("src", "util"), "xhtml"), "template.xsl");
String XSL = LocalResourceLoader.loadStylesheet(theGameKey);
String template = FileUtils.readFileAsString(templateFile);
return template.replace("###GAME_SPECIFIC_STUFF_HERE###", XSL);
}
//========IOstring code========
private static class IOString
{
private StringBuffer buf;
public IOString(String s) {
buf = new StringBuffer(s);
}
public String getString() {
return buf.toString();
}
public InputStream getInputStream() {
return new IOString.IOStringInputStream();
}
public OutputStream getOutputStream() {
return new IOString.IOStringOutputStream();
}
class IOStringInputStream extends java.io.InputStream {
private int position = 0;
public int read() throws java.io.IOException
{
if (position < buf.length()) {
return buf.charAt(position++);
} else {
return -1;
}
}
}
class IOStringOutputStream extends java.io.OutputStream {
public void write(int character) throws java.io.IOException {
buf.append((char)character);
}
}
}
}