1 /*------------------------------------------------------------------------------
2 Name: Transceiver.java
3 Project: xmlBlaster.org
4 Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
5 Comment: Demo code for a svg client using batik
6 Version: $Id: Transceiver.java 16476 2007-09-06 22:36:52Z laghi $
7 ------------------------------------------------------------------------------*/
8 package javaclients.svg.batik;
9
10 import org.apache.batik.gvt.GraphicsNode;
11 import org.apache.batik.bridge.BridgeContext;
12 import java.awt.Point;
13
14 import org.w3c.dom.Element;
15 import org.w3c.dom.Node;
16 import org.w3c.dom.Document;
17 import org.w3c.dom.NamedNodeMap;
18 import org.w3c.dom.NodeList;
19
20 import java.util.logging.Logger;
21 import java.util.logging.Level;
22 import org.xmlBlaster.util.Global;
23 import org.xmlBlaster.util.XmlBlasterException;
24
25 import org.xmlBlaster.client.I_XmlBlasterAccess;
26 import org.xmlBlaster.client.I_Callback;
27 import org.xmlBlaster.client.qos.PublishReturnQos;
28 import org.xmlBlaster.client.qos.ConnectQos;
29 import org.xmlBlaster.client.qos.DisconnectQos;
30 import org.xmlBlaster.client.key.UpdateKey;
31 import org.xmlBlaster.client.qos.UpdateQos;
32 import org.xmlBlaster.util.MsgUnit;
33 import org.xmlBlaster.util.XmlToDom;
34 import java.io.IOException;
35 import java.io.FileNotFoundException;
36 import java.io.FileInputStream;
37
38 import java.util.StringTokenizer;
39
40 /**
41 * @author $Author: laghi $ (michele@laghi.eu)
42 */
43
44 public class Transceiver implements I_Callback
45 {
46
47 private static final String ME = "Transceiver";
48 private final Global glob;
49 private static Logger log = Logger.getLogger(Transceiver.class.getName());
50 private BridgeContext bridgeContext = null;
51 private JSVGCanvasExtended canvas = null;
52 private I_XmlBlasterAccess xmlBlasterConnection = null;
53 private String svgFileName = null;
54
55 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>";
56 private final static String SVG_POSTFIX = "</svg>";
57
58 /**
59 * ensures that elements are subscribed only one time
60 */
61 private boolean elementsSubscribed = false;
62
63
64 protected static byte[] readFromFile (String filename)
65 throws FileNotFoundException, IOException
66 {
67 FileInputStream fileInputStream = new FileInputStream(filename);
68 int fileSize = fileInputStream.available();
69 byte[] b = new byte[fileSize];
70 fileInputStream.read(b);
71 return b;
72 }
73
74
75 public void subscribeElements () throws XmlBlasterException
76 {
77 if ((this.svgFileName != null) && (this.elementsSubscribed == false)) {
78 String xmlKey = "<?xml version='1.0' encoding='ISO-8859-1' ?>\n" +
79 "<key oid='' queryType='XPATH'>\n" +
80 " //svg[@name='" + this.svgFileName + "' and @complete='false']" +
81 "</key>";
82 String qos = "<qos></qos>";
83 this.xmlBlasterConnection.subscribe(xmlKey, qos);
84 this.elementsSubscribed = true;
85 }
86 }
87
88
89 /**
90 * In the argument list you should have either
91 * -svgMaster filename
92 * -svgSlave filename
93 */
94 public Transceiver (Global glob, JSVGCanvasExtended canvas)
95 {
96 this.glob = glob;
97
98 log.fine("constructor with Global");
99 this.canvas = canvas;
100 try {
101 String svgMaster = glob.getProperty().get("svgMaster", (String)null);
102 String svgSlave = glob.getProperty().get("svgSlave", (String)null);
103 String svgUser = glob.getProperty().get("svgUser", "dummyUser");
104
105 this.xmlBlasterConnection = glob.getXmlBlasterAccess();
106 ConnectQos connectQos = new ConnectQos(glob, svgUser, "secret");
107 this.xmlBlasterConnection.connect(connectQos, this);
108
109
110 if (svgMaster != null) {
111 this.svgFileName = svgMaster;
112 log.fine("constructor: running as Master: " + svgMaster);
113 // read the file, create a publishKey and then publish the thing
114 // then subscribe only to complete documents (not single elements)
115 // this because the elements can olny be updated once the document
116 // has been loaded.
117 String xmlKey = "<key oid=''><svg name='" + this.svgFileName + "' complete='true'></svg></key>";
118 String qos = "<qos></qos>";
119 // retrieve the file content
120 byte[] content = this.readFromFile(this.svgFileName);
121 MsgUnit messageUnit = new MsgUnit(xmlKey, content, qos);
122 PublishReturnQos ret = this.xmlBlasterConnection.publish(messageUnit);
123 log.info("constructor: " + ret.getKeyOid());
124 }
125 else if (svgSlave != null) {
126 this.svgFileName = svgSlave;
127 }
128
129 if (this.svgFileName != null) {
130 String xmlKey = "<?xml version='1.0' encoding='ISO-8859-1' ?>\n" +
131 "<key oid='' queryType='XPATH'>\n" +
132 " //svg[@name='" + this.svgFileName + "' and @complete='true']" +
133 "</key>";
134 String qos = "<qos></qos>";
135 this.xmlBlasterConnection.subscribe(xmlKey, qos);
136 }
137 }
138 // don't know if this is the correct place where to catch this exception
139 catch (XmlBlasterException ex) {
140 log.severe("Transceiver Constructor: " + ex.toString());
141 }
142 catch (FileNotFoundException ex) {
143 log.severe("Transceiver Constructor: " + ex.toString());
144 }
145 catch (IOException ex) {
146 log.severe("Transceiver Constructor: " + ex.toString());
147 }
148 }
149
150
151 /**
152 * Disconnects from the xmlBlaster Server
153 */
154 public void disconnect ()
155 {
156 this.xmlBlasterConnection.disconnect(new DisconnectQos(glob));
157 }
158
159
160 /**
161 * This method is invoked when a whole document is sent (this should be the
162 * first update this object should receive).
163 */
164 protected void updateDocument (byte[] content) throws XmlBlasterException
165 {
166 log.warning(".updateDocument()");
167 try {
168 this.canvas.loadDocumentFromByteArray(content);
169 }
170 catch (IOException ex)
171 {
172 log.severe("io exception: " + ex.toString());
173 throw new XmlBlasterException(ME, ".updateDocument: " + ex.getMessage());
174 }
175 }
176
177
178 protected void updateElement (byte[] content, String id) throws XmlBlasterException
179 {
180 log.fine(".updateElement(): " + id);
181
182 Element el = this.canvas.getElement(id);
183 if (el == null) {
184 log.warning("the element with id '" + id + "' is not registered");
185 return;
186 }
187
188 this.bridgeContext.unbind(el);
189 String completeSVG = SVG_PREFIX + new String(content) + SVG_POSTFIX;
190 log.warning(".updateElement: " + completeSVG);
191
192 try {
193 Node tempNode = SvgUtility.createDocument(completeSVG, "file://dummy.svg").getDocumentElement().getFirstChild();
194
195 Document doc = el.getOwnerDocument();
196 tempNode = doc.importNode(tempNode, true);
197
198 Node parentNode = el.getParentNode();
199 parentNode.replaceChild(tempNode, el);
200
201 GraphicsNode graphicsNode = this.bridgeContext.getGVTBuilder().build(this.bridgeContext, (Element)tempNode);
202 this.bridgeContext.bind((Element)tempNode, graphicsNode);
203
204 graphicsNode = this.bridgeContext.getGVTBuilder().build(this.bridgeContext, tempNode.getOwnerDocument());
205 this.canvas.setGraphicsNode(graphicsNode);
206 this.canvas.updateDocument();
207 }
208 catch (IOException ex) {
209 log.severe(".updateElement: IOException " + ex.getMessage());
210 throw new XmlBlasterException(ME, ".updateElement: IOException " + ex.getMessage());
211 }
212 }
213
214
215
216 public String update(java.lang.String loginName, UpdateKey updateKey,
217 byte[] content, UpdateQos updateQos) throws XmlBlasterException
218 {
219 log.fine("update called, content: " + new String(content));
220 log.fine("update called, loginName: " + loginName);
221 log.fine("update called, updateKey: " + updateKey);
222 log.fine("update called, updateQos: " + updateQos);
223
224 NodeList nodes = XmlToDom.parseToDomTree(glob, updateKey.toString()).getDocumentElement().getElementsByTagName("svg");
225 int length = nodes.getLength();
226 if ((nodes == null) || (length < 1)) {
227 throw new XmlBlasterException(ME, ".update: no svg node found in the key");
228 }
229
230 Node keyNode = nodes.item(0);
231 if (keyNode instanceof Element) {
232 // if an id has been defined, then it is a single element otherwise the whole document
233 String id = ((Element)keyNode).getAttribute("id");
234 if ((id == null) || (id.length() < 1)) {
235 updateDocument(content);
236 }
237 else updateElement(content, id);
238 }
239 else throw new XmlBlasterException(ME, "update: svg node is not an element");
240 return "";
241 }
242
243
244
245 public void setBridgeContext (BridgeContext bridgeContext)
246 {
247 this.bridgeContext = bridgeContext;
248 }
249
250
251 /**
252 * finds if the given element is in the idTable (i.e. if it does not
253 * fullfill the 'dynamic' requirements). If not, its parent is searched and
254 * so on. If the element nor one of its ancestors is dynamic, it returns
255 * null, otherwise it returns the first dynamic element found.
256 *
257 * Currently the requirements for dynamic is that the element has an id
258 * which has a prefix 'xmlBlaster:'
259 */
260 protected Element getClickedDynamicElement (Element el)
261 {
262 log.fine(".getClickedDynamicElement");
263 while (el != null) {
264 String id = el.getAttribute("id");
265 if (SvgIdMapper.isDynamic(id)) return el;
266 Node parent = el.getParentNode();
267 if (parent instanceof Element) el = (Element)parent;
268 else return null;
269 }
270 return null;
271 }
272
273
274 public static void moveElement (Element el, int x, int y)
275 {
276 //log.finer(".moveElement");
277 if (el == null) return;
278 String transformString = el.getAttribute("transform");
279 if ((transformString == null) || (transformString.length() < 1)) {
280 transformString = "translate(" + x + "," + y + ")";
281 }
282 else {
283 el.removeAttribute("transform");
284 // check if 'translate' is defined
285 int pos = transformString.indexOf("translate(");
286 if (pos < 0) transformString += " translate(" + x + "," + y + ")";
287 else { // get the position string
288 String endString = transformString.substring(pos + "translate(".length()).trim();
289 String startString = transformString.substring(0, pos).trim();
290 String coreString = endString.substring(0, endString.indexOf(")")).trim();
291 endString = endString.substring(1 + endString.indexOf(")")).trim();
292 StringTokenizer tokenizer = new StringTokenizer(coreString,",");
293
294 double x0 = Double.parseDouble(tokenizer.nextToken().trim());
295 double y0 = Double.parseDouble(tokenizer.nextToken().trim());
296
297 transformString = startString + " translate(" + (x0 + x) + "," + (y0 + y) + ") " + endString;
298 }
299 }
300 el.setAttribute("transform", transformString);
301 }
302
303 public void move (GraphicsNode graphicsNode, Point displacement)
304 {
305 log.info(".move " + displacement.x + " " + displacement.y);
306
307 Element el = this.bridgeContext.getElement(graphicsNode);
308 el = getClickedDynamicElement(el);
309 if (el == null) return;
310
311 String id = el.getAttribute("id");
312
313 // later make the necessary transform ....
314 this.moveElement(el, displacement.x, displacement.y);
315
316 /*
317 double x = graphicsNode.getBounds().getMinX() + displacement.x;
318 double y = graphicsNode.getBounds().getMinY() + displacement.y;
319
320 el.setAttributeNS(null, "x", Double.toString(x));
321 el.setAttributeNS(null, "y", Double.toString(y));
322 */
323 log.info(".move: el: " + el.getNodeName());
324
325
326 // publish the node
327 String xmlKey = "<key oid=''><svg name='" + this.svgFileName + "' id='" + id + "' complete='false'></svg></key>";
328 String qos = "<qos></qos>";
329 // retrieve the file content
330 byte[] content = XmlUtility.write(el).getBytes();
331
332 try {
333 MsgUnit messageUnit = new MsgUnit(xmlKey, content, qos);
334 PublishReturnQos ret = this.xmlBlasterConnection.publish(messageUnit);
335 log.fine("move: " + ret.getKeyOid());
336 }
337 catch (XmlBlasterException ex) {
338 log.severe(".move exception when publishing: " + ex.getMessage());
339 }
340 }
341
342 }
syntax highlighted by Code2HTML, v. 0.9.1