/*------------------------------------------------------------------------------
Name: Transceiver.java
Project: xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
Comment: Demo code for a svg client using batik
Version: $Id: Transceiver.java 16476 2007-09-06 22:36:52Z laghi $
------------------------------------------------------------------------------*/
package javaclients.svg.batik;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.bridge.BridgeContext;
import java.awt.Point;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import java.util.logging.Logger;
import java.util.logging.Level;
import org.xmlBlaster.util.Global;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.client.I_XmlBlasterAccess;
import org.xmlBlaster.client.I_Callback;
import org.xmlBlaster.client.qos.PublishReturnQos;
import org.xmlBlaster.client.qos.ConnectQos;
import org.xmlBlaster.client.qos.DisconnectQos;
import org.xmlBlaster.client.key.UpdateKey;
import org.xmlBlaster.client.qos.UpdateQos;
import org.xmlBlaster.util.MsgUnit;
import org.xmlBlaster.util.XmlToDom;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.FileInputStream;
import java.util.StringTokenizer;
/**
* @author $Author: laghi $ (michele@laghi.eu)
*/
public class Transceiver implements I_Callback
{
private static final String ME = "Transceiver";
private final Global glob;
private static Logger log = Logger.getLogger(Transceiver.class.getName());
private BridgeContext bridgeContext = null;
private JSVGCanvasExtended canvas = null;
private I_XmlBlasterAccess xmlBlasterConnection = null;
private String svgFileName = null;
private final static String SVG_PREFIX = "<?xml version='1.0' standalone='no'?>\n<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'\n'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'><svg>";
private final static String SVG_POSTFIX = "</svg>";
/**
* ensures that elements are subscribed only one time
*/
private boolean elementsSubscribed = false;
protected static byte[] readFromFile (String filename)
throws FileNotFoundException, IOException
{
FileInputStream fileInputStream = new FileInputStream(filename);
int fileSize = fileInputStream.available();
byte[] b = new byte[fileSize];
fileInputStream.read(b);
return b;
}
public void subscribeElements () throws XmlBlasterException
{
if ((this.svgFileName != null) && (this.elementsSubscribed == false)) {
String xmlKey = "<?xml version='1.0' encoding='ISO-8859-1' ?>\n" +
"<key oid='' queryType='XPATH'>\n" +
" //svg[@name='" + this.svgFileName + "' and @complete='false']" +
"</key>";
String qos = "<qos></qos>";
this.xmlBlasterConnection.subscribe(xmlKey, qos);
this.elementsSubscribed = true;
}
}
/**
* In the argument list you should have either
* -svgMaster filename
* -svgSlave filename
*/
public Transceiver (Global glob, JSVGCanvasExtended canvas)
{
this.glob = glob;
log.fine("constructor with Global");
this.canvas = canvas;
try {
String svgMaster = glob.getProperty().get("svgMaster", (String)null);
String svgSlave = glob.getProperty().get("svgSlave", (String)null);
String svgUser = glob.getProperty().get("svgUser", "dummyUser");
this.xmlBlasterConnection = glob.getXmlBlasterAccess();
ConnectQos connectQos = new ConnectQos(glob, svgUser, "secret");
this.xmlBlasterConnection.connect(connectQos, this);
if (svgMaster != null) {
this.svgFileName = svgMaster;
log.fine("constructor: running as Master: " + svgMaster);
// read the file, create a publishKey and then publish the thing
// then subscribe only to complete documents (not single elements)
// this because the elements can olny be updated once the document
// has been loaded.
String xmlKey = "<key oid=''><svg name='" + this.svgFileName + "' complete='true'></svg></key>";
String qos = "<qos></qos>";
// retrieve the file content
byte[] content = this.readFromFile(this.svgFileName);
MsgUnit messageUnit = new MsgUnit(xmlKey, content, qos);
PublishReturnQos ret = this.xmlBlasterConnection.publish(messageUnit);
log.info("constructor: " + ret.getKeyOid());
}
else if (svgSlave != null) {
this.svgFileName = svgSlave;
}
if (this.svgFileName != null) {
String xmlKey = "<?xml version='1.0' encoding='ISO-8859-1' ?>\n" +
"<key oid='' queryType='XPATH'>\n" +
" //svg[@name='" + this.svgFileName + "' and @complete='true']" +
"</key>";
String qos = "<qos></qos>";
this.xmlBlasterConnection.subscribe(xmlKey, qos);
}
}
// don't know if this is the correct place where to catch this exception
catch (XmlBlasterException ex) {
log.severe("Transceiver Constructor: " + ex.toString());
}
catch (FileNotFoundException ex) {
log.severe("Transceiver Constructor: " + ex.toString());
}
catch (IOException ex) {
log.severe("Transceiver Constructor: " + ex.toString());
}
}
/**
* Disconnects from the xmlBlaster Server
*/
public void disconnect ()
{
this.xmlBlasterConnection.disconnect(new DisconnectQos(glob));
}
/**
* This method is invoked when a whole document is sent (this should be the
* first update this object should receive).
*/
protected void updateDocument (byte[] content) throws XmlBlasterException
{
log.warning(".updateDocument()");
try {
this.canvas.loadDocumentFromByteArray(content);
}
catch (IOException ex)
{
log.severe("io exception: " + ex.toString());
throw new XmlBlasterException(ME, ".updateDocument: " + ex.getMessage());
}
}
protected void updateElement (byte[] content, String id) throws XmlBlasterException
{
log.fine(".updateElement(): " + id);
Element el = this.canvas.getElement(id);
if (el == null) {
log.warning("the element with id '" + id + "' is not registered");
return;
}
this.bridgeContext.unbind(el);
String completeSVG = SVG_PREFIX + new String(content) + SVG_POSTFIX;
log.warning(".updateElement: " + completeSVG);
try {
Node tempNode = SvgUtility.createDocument(completeSVG, "file://dummy.svg").getDocumentElement().getFirstChild();
Document doc = el.getOwnerDocument();
tempNode = doc.importNode(tempNode, true);
Node parentNode = el.getParentNode();
parentNode.replaceChild(tempNode, el);
GraphicsNode graphicsNode = this.bridgeContext.getGVTBuilder().build(this.bridgeContext, (Element)tempNode);
this.bridgeContext.bind((Element)tempNode, graphicsNode);
graphicsNode = this.bridgeContext.getGVTBuilder().build(this.bridgeContext, tempNode.getOwnerDocument());
this.canvas.setGraphicsNode(graphicsNode);
this.canvas.updateDocument();
}
catch (IOException ex) {
log.severe(".updateElement: IOException " + ex.getMessage());
throw new XmlBlasterException(ME, ".updateElement: IOException " + ex.getMessage());
}
}
public String update(java.lang.String loginName, UpdateKey updateKey,
byte[] content, UpdateQos updateQos) throws XmlBlasterException
{
log.fine("update called, content: " + new String(content));
log.fine("update called, loginName: " + loginName);
log.fine("update called, updateKey: " + updateKey);
log.fine("update called, updateQos: " + updateQos);
NodeList nodes = XmlToDom.parseToDomTree(glob, updateKey.toString()).getDocumentElement().getElementsByTagName("svg");
int length = nodes.getLength();
if ((nodes == null) || (length < 1)) {
throw new XmlBlasterException(ME, ".update: no svg node found in the key");
}
Node keyNode = nodes.item(0);
if (keyNode instanceof Element) {
// if an id has been defined, then it is a single element otherwise the whole document
String id = ((Element)keyNode).getAttribute("id");
if ((id == null) || (id.length() < 1)) {
updateDocument(content);
}
else updateElement(content, id);
}
else throw new XmlBlasterException(ME, "update: svg node is not an element");
return "";
}
public void setBridgeContext (BridgeContext bridgeContext)
{
this.bridgeContext = bridgeContext;
}
/**
* finds if the given element is in the idTable (i.e. if it does not
* fullfill the 'dynamic' requirements). If not, its parent is searched and
* so on. If the element nor one of its ancestors is dynamic, it returns
* null, otherwise it returns the first dynamic element found.
*
* Currently the requirements for dynamic is that the element has an id
* which has a prefix 'xmlBlaster:'
*/
protected Element getClickedDynamicElement (Element el)
{
log.fine(".getClickedDynamicElement");
while (el != null) {
String id = el.getAttribute("id");
if (SvgIdMapper.isDynamic(id)) return el;
Node parent = el.getParentNode();
if (parent instanceof Element) el = (Element)parent;
else return null;
}
return null;
}
public static void moveElement (Element el, int x, int y)
{
//log.finer(".moveElement");
if (el == null) return;
String transformString = el.getAttribute("transform");
if ((transformString == null) || (transformString.length() < 1)) {
transformString = "translate(" + x + "," + y + ")";
}
else {
el.removeAttribute("transform");
// check if 'translate' is defined
int pos = transformString.indexOf("translate(");
if (pos < 0) transformString += " translate(" + x + "," + y + ")";
else { // get the position string
String endString = transformString.substring(pos + "translate(".length()).trim();
String startString = transformString.substring(0, pos).trim();
String coreString = endString.substring(0, endString.indexOf(")")).trim();
endString = endString.substring(1 + endString.indexOf(")")).trim();
StringTokenizer tokenizer = new StringTokenizer(coreString,",");
double x0 = Double.parseDouble(tokenizer.nextToken().trim());
double y0 = Double.parseDouble(tokenizer.nextToken().trim());
transformString = startString + " translate(" + (x0 + x) + "," + (y0 + y) + ") " + endString;
}
}
el.setAttribute("transform", transformString);
}
public void move (GraphicsNode graphicsNode, Point displacement)
{
log.info(".move " + displacement.x + " " + displacement.y);
Element el = this.bridgeContext.getElement(graphicsNode);
el = getClickedDynamicElement(el);
if (el == null) return;
String id = el.getAttribute("id");
// later make the necessary transform ....
this.moveElement(el, displacement.x, displacement.y);
/*
double x = graphicsNode.getBounds().getMinX() + displacement.x;
double y = graphicsNode.getBounds().getMinY() + displacement.y;
el.setAttributeNS(null, "x", Double.toString(x));
el.setAttributeNS(null, "y", Double.toString(y));
*/
log.info(".move: el: " + el.getNodeName());
// publish the node
String xmlKey = "<key oid=''><svg name='" + this.svgFileName + "' id='" + id + "' complete='false'></svg></key>";
String qos = "<qos></qos>";
// retrieve the file content
byte[] content = XmlUtility.write(el).getBytes();
try {
MsgUnit messageUnit = new MsgUnit(xmlKey, content, qos);
PublishReturnQos ret = this.xmlBlasterConnection.publish(messageUnit);
log.fine("move: " + ret.getKeyOid());
}
catch (XmlBlasterException ex) {
log.severe(".move exception when publishing: " + ex.getMessage());
}
}
}