View Javadoc

1   /*
2    *  Copyright 2006 Simon Raess
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
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 			// ignored			
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 			// ignored			
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 			// ignored			
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 			// ignored
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 }