1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.beep4j.internal.profile;
17
18 import static net.sf.beep4j.internal.profile.XMLConstants.A_CODE;
19 import static net.sf.beep4j.internal.profile.XMLConstants.A_ENCODING;
20 import static net.sf.beep4j.internal.profile.XMLConstants.A_NUMBER;
21 import static net.sf.beep4j.internal.profile.XMLConstants.A_URI;
22 import static net.sf.beep4j.internal.profile.XMLConstants.ENCODING_BASE64;
23 import static net.sf.beep4j.internal.profile.XMLConstants.E_CLOSE;
24 import static net.sf.beep4j.internal.profile.XMLConstants.E_ERROR;
25 import static net.sf.beep4j.internal.profile.XMLConstants.E_GREETING;
26 import static net.sf.beep4j.internal.profile.XMLConstants.E_OK;
27 import static net.sf.beep4j.internal.profile.XMLConstants.E_PROFILE;
28 import static net.sf.beep4j.internal.profile.XMLConstants.E_START;
29
30 import java.io.IOException;
31 import java.io.Reader;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.LinkedHashSet;
36 import java.util.LinkedList;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40
41 import javax.xml.parsers.ParserConfigurationException;
42 import javax.xml.parsers.SAXParser;
43 import javax.xml.parsers.SAXParserFactory;
44
45 import net.sf.beep4j.Message;
46 import net.sf.beep4j.ProfileInfo;
47 import net.sf.beep4j.ProtocolException;
48 import net.sf.beep4j.internal.InternalException;
49 import net.sf.beep4j.internal.util.Base64Encoder;
50
51 import org.xml.sax.Attributes;
52 import org.xml.sax.InputSource;
53 import org.xml.sax.SAXException;
54 import org.xml.sax.helpers.DefaultHandler;
55
56 public class SaxMessageParser implements ChannelManagementMessageParser {
57
58 public ChannelManagementRequest parseRequest(Message message) {
59 try {
60 ElementHandlerContentHandler handler = new ElementHandlerContentHandler();
61 handler.registerHandler("/start", new StartElementHandler(handler));
62 handler.registerHandler("/start/profile", new ProfileElementHandler(handler));
63 handler.registerHandler("/close", new CloseElementHandler(handler));
64
65 SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
66 parser.parse(new InputSource(message.getReader()), handler);
67 return (ChannelManagementRequest) handler.peekObject();
68
69 } catch (Exception e) {
70 throw new InternalException(e);
71 }
72 }
73
74 public BEEPError parseError(Message message) {
75 ElementHandlerContentHandler handler = new ElementHandlerContentHandler();
76 handler.registerHandler("/error", new ErrorElementHandler(handler));
77 try {
78 SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
79 parser.parse(new InputSource(message.getReader()), handler);
80 return (BEEPError) handler.peekObject();
81 } catch (Exception e) {
82 throw new InternalException(e);
83 }
84 }
85
86 public void parseOk(Message message) {
87 ElementHandlerContentHandler handler = new ElementHandlerContentHandler();
88 handler.registerHandler("/ok", new OkElementHandler());
89 try {
90 SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
91 parser.parse(new InputSource(message.getReader()), handler);
92 } catch (Exception e) {
93 throw new InternalException(e);
94 }
95 }
96
97 public Greeting parseGreeting(Message message) {
98 ElementHandlerContentHandler handler = new ElementHandlerContentHandler();
99 handler.registerHandler("/greeting", new GreetingElementHandler(handler));
100 handler.registerHandler("/greeting/profile", new SimpleProfileElementHandler(handler));
101 try {
102 SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
103 parser.parse(new InputSource(message.getReader()), handler);
104 return (Greeting) handler.peekObject();
105 } catch (Exception e) {
106 throw new InternalException(e);
107 }
108 }
109
110 @SuppressWarnings("unchecked")
111 public ProfileInfo parseProfile(Message message) {
112 ElementHandlerContentHandler handler = new ElementHandlerContentHandler();
113 handler.registerHandler("/profile", new ProfileElementHandler(handler));
114 handler.pushObject(new LinkedList<ProfileInfo>());
115 try {
116 SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
117 parser.parse(new InputSource(message.getReader()), handler);
118 List<ProfileInfo> result = (List) handler.peekObject();
119 return result.get(0);
120 } catch (Exception e) {
121 throw new InternalException(e);
122 }
123 }
124
125 protected StartChannelMessage parseStart(Reader reader) throws ParserConfigurationException, SAXException, IOException {
126 ElementHandlerContentHandler handler = new ElementHandlerContentHandler();
127 handler.registerHandler("/start", new StartElementHandler(handler));
128 SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
129 parser.parse(new InputSource(reader), handler);
130 return (StartChannelMessage) handler.peekObject();
131 }
132
133 protected static class ElementHandlerContentHandler extends DefaultHandler implements ElementHandlerContext {
134
135 private final LinkedList<String> path = new LinkedList<String>();
136
137 private LinkedList<Object> objectStack = new LinkedList<Object>();
138
139 private Map<String,ElementHandler> handlers = new HashMap<String,ElementHandler>();
140
141 public Object peekObject() {
142 return objectStack.getLast();
143 }
144
145 public Object popObject() {
146 return objectStack.removeLast();
147 }
148
149 public void pushObject(Object o) {
150 objectStack.addLast(o);
151 }
152
153 public void registerHandler(String path, ElementHandler handler) {
154 handlers.put(path, handler);
155 }
156
157 private ElementHandler getElementHandler(List<String> path) {
158 LinkedList<String> copy = new LinkedList<String>(path);
159 while (copy.size() > 0) {
160 String key = toString(copy);
161 ElementHandler handler = handlers.get(key);
162 if (handler != null) {
163 return handler;
164 }
165 copy.removeLast();
166 }
167 return null;
168 }
169
170 private String toString(List<String> path) {
171 StringBuilder result = new StringBuilder();
172 Iterator<String> it = path.iterator();
173 while (it.hasNext()) {
174 String fragment = (String) it.next();
175 result.append("/").append(fragment);
176 }
177 return result.toString();
178 }
179
180 private Map<String,String> toMap(Attributes attributes) {
181 Map<String,String> result = new HashMap<String,String>();
182 for (int i = 0; i < attributes.getLength(); i++) {
183 result.put(attributes.getQName(i), attributes.getValue(i));
184 }
185 return result;
186 }
187
188 @Override
189 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
190 path.addLast(qName);
191 ElementHandler handler = getElementHandler(path);
192 handler.startElement(qName, toMap(attributes));
193 }
194
195 @Override
196 public void characters(char[] ch, int start, int length) throws SAXException {
197 ElementHandler handler = getElementHandler(path);
198 handler.characters(new String(ch, start, length));
199 }
200
201 @Override
202 public void endElement(String uri, String localName, String qName) throws SAXException {
203 ElementHandler handler = getElementHandler(path);
204 handler.endElement();
205 path.removeLast();
206 }
207 }
208
209 protected static class StartElementHandler implements ElementHandler {
210
211 private final ElementHandlerContext context;
212
213 private int channelNumber;
214
215 public StartElementHandler(ElementHandlerContext context) {
216 this.context = context;
217 }
218
219 public void startElement(String name, Map<String,String> attributes) {
220 expectName(E_START, name);
221 channelNumber = expectIntegerAttribute(A_NUMBER, attributes);
222 context.pushObject(new LinkedList<ProfileInfo>());
223 }
224
225 public void characters(String content) {
226
227 }
228
229 @SuppressWarnings("unchecked")
230 public void endElement() {
231 List<ProfileInfo> profiles = (List) context.popObject();
232 context.pushObject(new StartChannelMessage(
233 channelNumber,
234 profiles.toArray(new ProfileInfo[profiles.size()])));
235 }
236
237 }
238
239 protected static class CloseElementHandler implements ElementHandler {
240
241 private final ElementHandlerContext context;
242
243 private int code;
244
245 private int number;
246
247 private StringBuilder content;
248
249 public CloseElementHandler(ElementHandlerContext context) {
250 this.context = context;
251 }
252
253 public void startElement(String name, Map<String, String> attributes) {
254 expectName(E_CLOSE, name);
255 this.code = expectIntegerAttribute(A_CODE, attributes);
256 this.number = getIntegerAttribute(A_NUMBER, 0, attributes);
257 this.content = new StringBuilder();
258 }
259
260 public void characters(String content) {
261 this.content.append(content);
262 }
263
264 public void endElement() {
265 context.pushObject(new CloseChannelMessage(number, code, content.toString()));
266 }
267
268 }
269
270 protected static class GreetingElementHandler implements ElementHandler {
271
272 private final ElementHandlerContext context;
273
274 private String[] localize;
275
276 private String[] features;
277
278 public GreetingElementHandler(ElementHandlerContext context) {
279 this.context = context;
280 }
281
282 public void startElement(String name, Map<String, String> attributes) {
283 if (!E_GREETING.equals(name)) {
284 throw new ProtocolException("expected greeting element, was '" + name + "'");
285 }
286 String value = attributes.get("localize");
287 this.localize = value == null ? new String[0] : value.split(" ");
288
289 value = attributes.get("features");
290 this.features = value == null ? new String[0] : value.split(" ");
291
292 context.pushObject(new LinkedHashSet<String>());
293 }
294
295 public void characters(String content) {
296
297 }
298
299 @SuppressWarnings("unchecked")
300 public void endElement() {
301 Set<String> profiles = (Set<String>) context.popObject();
302 context.pushObject(new Greeting(localize, features, profiles.toArray(new String[0])));
303 }
304
305 }
306
307 protected static class ProfileElementHandler implements ElementHandler {
308
309 private final ElementHandlerContext context;
310
311 private String uri;
312
313 private boolean base64Encoded;
314
315 private StringBuilder content;
316
317 public ProfileElementHandler(ElementHandlerContext context) {
318 this.context = context;
319 }
320
321 public void startElement(String name, Map<String, String> attributes) {
322 expectName(E_PROFILE, name);
323 this.uri = expectAttribute(A_URI, attributes);
324 this.base64Encoded = getBooleanAttribute(A_ENCODING, ENCODING_BASE64, attributes);
325 this.content = new StringBuilder();
326 }
327
328 public void characters(String content) {
329 this.content.append(content);
330 }
331
332 @SuppressWarnings("unchecked")
333 public void endElement() {
334 ProfileInfo profile = null;
335 String content = this.content.toString().trim();
336 if (content.length() == 0) {
337 profile = new ProfileInfo(uri);
338 } else if (base64Encoded) {
339 byte[] bytes = new Base64Encoder().decode(content);
340 profile = new ProfileInfo(uri, bytes);
341 } else {
342 profile = new ProfileInfo(uri, content);
343 }
344 ((Collection) context.peekObject()).add(profile);
345 }
346
347 }
348
349 protected static class SimpleProfileElementHandler implements ElementHandler {
350
351 private final ElementHandlerContext context;
352
353 public SimpleProfileElementHandler(ElementHandlerContext context) {
354 this.context = context;
355 }
356
357 @SuppressWarnings("unchecked")
358 public void startElement(String name, Map<String, String> attributes) {
359 expectName(E_PROFILE, name);
360 String uri = expectAttribute(A_URI, attributes);
361 ((Set) context.peekObject()).add(uri);
362 }
363
364 public void characters(String content) {
365 throw new ProtocolException("profile element inside greeting must not contain content");
366 }
367
368 public void endElement() {
369
370 }
371
372 }
373
374 protected static class OkElementHandler implements ElementHandler {
375
376 public void startElement(String name, Map<String, String> attributes) {
377 expectName(E_OK, name);
378 }
379
380 public void characters(String content) {
381 throw new ProtocolException("ok element must no contain content");
382 }
383
384 public void endElement() {
385
386 }
387
388 }
389
390 protected static class ErrorElementHandler implements ElementHandler {
391
392 private final ElementHandlerContext context;
393
394 private int code;
395
396 private StringBuilder content;
397
398 public ErrorElementHandler(ElementHandlerContext context) {
399 this.context = context;
400 }
401
402 public void startElement(String name, Map<String, String> attributes) {
403 expectName(E_ERROR, name);
404 this.code = expectIntegerAttribute(A_CODE, attributes);
405 this.content = new StringBuilder();
406 }
407
408 public void characters(String content) {
409 this.content.append(content);
410 }
411
412 public void endElement() {
413 context.pushObject(new BEEPError(code, content.toString().trim()));
414 }
415
416 }
417
418
419 private static void expectName(String expected, String actual) {
420 if (!expected.equals(actual)) {
421 throw new ProtocolException("expected element '" + expected + "' "
422 + "but was '" + actual + "'");
423 }
424 }
425
426 private static int getIntegerAttribute(String name, int defaultValue, Map<String,String> attributes) {
427 String value = attributes.get(name);
428 if (value == null) {
429 return defaultValue;
430 }
431 try {
432 return Integer.parseInt(value);
433 } catch (NumberFormatException e) {
434 return defaultValue;
435 }
436 }
437
438 private static boolean getBooleanAttribute(String name, String trueValue, Map<String,String> attributes) {
439 String value = attributes.get(name);
440 return trueValue.equals(value);
441 }
442
443 private static int expectIntegerAttribute(String name, Map<String,String> attributes) {
444 String value = attributes.get(name);
445 if (value == null) {
446 throw new ProtocolException("expected mandatory attribute: " + name);
447 }
448 try {
449 return Integer.parseInt(value);
450 } catch (NumberFormatException e) {
451 throw new ProtocolException("expected attribute is not a number: " + name
452 + " (value=" + value + ")");
453 }
454 }
455
456 private static String expectAttribute(String name, Map<String,String> attributes) {
457 String value = attributes.get(name);
458 if (value == null) {
459 throw new ProtocolException("expected mandatory attribute '" + name + "'");
460 }
461 return value;
462 }
463
464 }