1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.fourthline.cling.binding.xml;
17
18 import org.fourthline.cling.binding.staging.MutableDevice;
19 import org.fourthline.cling.binding.staging.MutableIcon;
20 import org.fourthline.cling.binding.staging.MutableService;
21 import org.fourthline.cling.binding.staging.MutableUDAVersion;
22 import org.fourthline.cling.model.ValidationException;
23 import org.fourthline.cling.model.XMLUtil;
24 import org.fourthline.cling.model.meta.Device;
25 import org.fourthline.cling.model.types.DLNACaps;
26 import org.fourthline.cling.model.types.DLNADoc;
27 import org.fourthline.cling.model.types.InvalidValueException;
28 import org.fourthline.cling.model.types.ServiceId;
29 import org.fourthline.cling.model.types.ServiceType;
30 import org.fourthline.cling.model.types.UDN;
31 import org.seamless.util.MimeType;
32 import org.seamless.xml.SAXParser;
33 import org.xml.sax.Attributes;
34 import org.xml.sax.InputSource;
35 import org.xml.sax.SAXException;
36
37 import java.io.StringReader;
38 import java.net.URL;
39 import java.util.ArrayList;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.logging.Logger;
43
44 import static org.fourthline.cling.binding.xml.Descriptor.Device.ELEMENT;
45
46
47
48
49
50
51 public class UDA10DeviceDescriptorBinderSAXImpl extends UDA10DeviceDescriptorBinderImpl {
52
53 private static Logger log = Logger.getLogger(DeviceDescriptorBinder.class.getName());
54
55 @Override
56 public <D extends Device> D describe(D undescribedDevice, String descriptorXml) throws DescriptorBindingException, ValidationException {
57
58 if (descriptorXml == null || descriptorXml.length() == 0) {
59 throw new DescriptorBindingException("Null or empty descriptor");
60 }
61
62 try {
63 log.fine("Populating device from XML descriptor: " + undescribedDevice);
64
65
66
67 SAXParser parser = new SAXParser();
68
69 MutableDevice descriptor = new MutableDevice();
70 new RootHandler(descriptor, parser);
71
72 parser.parse(
73 new InputSource(
74
75 new StringReader(descriptorXml.trim())
76 )
77 );
78
79
80 return (D) descriptor.build(undescribedDevice);
81
82 } catch (ValidationException ex) {
83 throw ex;
84 } catch (Exception ex) {
85 throw new DescriptorBindingException("Could not parse device descriptor: " + ex.toString(), ex);
86 }
87 }
88
89 protected static class RootHandler extends DeviceDescriptorHandler<MutableDevice> {
90
91 public RootHandler(MutableDevice instance, SAXParser parser) {
92 super(instance, parser);
93 }
94
95 @Override
96 public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
97
98 if (element.equals(SpecVersionHandler.EL)) {
99 MutableUDAVersion udaVersion = new MutableUDAVersion();
100 getInstance().udaVersion = udaVersion;
101 new SpecVersionHandler(udaVersion, this);
102 }
103
104 if (element.equals(DeviceHandler.EL)) {
105 new DeviceHandler(getInstance(), this);
106 }
107
108 }
109
110 @Override
111 public void endElement(ELEMENT element) throws SAXException {
112 switch (element) {
113 case URLBase:
114 try {
115 String urlString = getCharacters();
116 if (urlString != null && urlString.length() > 0) {
117
118 getInstance().baseURL = new URL(urlString);
119 }
120 } catch (Exception ex) {
121 throw new SAXException("Invalid URLBase: " + ex.toString());
122 }
123 break;
124 }
125 }
126 }
127
128 protected static class SpecVersionHandler extends DeviceDescriptorHandler<MutableUDAVersion> {
129
130 public static final ELEMENT EL = ELEMENT.specVersion;
131
132 public SpecVersionHandler(MutableUDAVersion instance, DeviceDescriptorHandler parent) {
133 super(instance, parent);
134 }
135
136 @Override
137 public void endElement(ELEMENT element) throws SAXException {
138 switch (element) {
139 case major:
140 String majorVersion = getCharacters().trim();
141 if (!majorVersion.equals("1")) {
142 log.warning("Unsupported UDA major version, ignoring: " + majorVersion);
143 majorVersion = "1";
144 }
145 getInstance().major = Integer.valueOf(majorVersion);
146 break;
147 case minor:
148 String minorVersion = getCharacters().trim();
149 if (!minorVersion.equals("0")) {
150 log.warning("Unsupported UDA minor version, ignoring: " + minorVersion);
151 minorVersion = "0";
152 }
153 getInstance().minor = Integer.valueOf(minorVersion);
154 break;
155 }
156 }
157
158 @Override
159 public boolean isLastElement(ELEMENT element) {
160 return element.equals(EL);
161 }
162 }
163
164 protected static class DeviceHandler extends DeviceDescriptorHandler<MutableDevice> {
165
166 public static final ELEMENT EL = ELEMENT.device;
167
168 public DeviceHandler(MutableDevice instance, DeviceDescriptorHandler parent) {
169 super(instance, parent);
170 }
171
172 @Override
173 public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
174
175 if (element.equals(IconListHandler.EL)) {
176 List<MutableIcon> icons = new ArrayList<>();
177 getInstance().icons = icons;
178 new IconListHandler(icons, this);
179 }
180
181 if (element.equals(ServiceListHandler.EL)) {
182 List<MutableService> services = new ArrayList<>();
183 getInstance().services = services;
184 new ServiceListHandler(services, this);
185 }
186
187 if (element.equals(DeviceListHandler.EL)) {
188 List<MutableDevice> devices = new ArrayList<>();
189 getInstance().embeddedDevices = devices;
190 new DeviceListHandler(devices, this);
191 }
192 }
193
194 @Override
195 public void endElement(ELEMENT element) throws SAXException {
196 switch (element) {
197 case deviceType:
198 getInstance().deviceType = getCharacters();
199 break;
200 case friendlyName:
201 getInstance().friendlyName = getCharacters();
202 break;
203 case manufacturer:
204 getInstance().manufacturer = getCharacters();
205 break;
206 case manufacturerURL:
207 getInstance().manufacturerURI = parseURI(getCharacters());
208 break;
209 case modelDescription:
210 getInstance().modelDescription = getCharacters();
211 break;
212 case modelName:
213 getInstance().modelName = getCharacters();
214 break;
215 case modelNumber:
216 getInstance().modelNumber = getCharacters();
217 break;
218 case modelURL:
219 getInstance().modelURI = parseURI(getCharacters());
220 break;
221 case presentationURL:
222 getInstance().presentationURI = parseURI(getCharacters());
223 break;
224 case UPC:
225 getInstance().upc = getCharacters();
226 break;
227 case serialNumber:
228 getInstance().serialNumber = getCharacters();
229 break;
230 case UDN:
231 getInstance().udn = UDN.valueOf(getCharacters());
232 break;
233 case X_DLNADOC:
234 String txt = getCharacters();
235 try {
236 getInstance().dlnaDocs.add(DLNADoc.valueOf(txt));
237 } catch (InvalidValueException ex) {
238 log.info("Invalid X_DLNADOC value, ignoring value: " + txt);
239 }
240 break;
241 case X_DLNACAP:
242 getInstance().dlnaCaps = DLNACaps.valueOf(getCharacters());
243 break;
244 }
245 }
246
247 @Override
248 public boolean isLastElement(ELEMENT element) {
249 return element.equals(EL);
250 }
251 }
252
253 protected static class IconListHandler extends DeviceDescriptorHandler<List<MutableIcon>> {
254
255 public static final ELEMENT EL = ELEMENT.iconList;
256
257 public IconListHandler(List<MutableIcon> instance, DeviceDescriptorHandler parent) {
258 super(instance, parent);
259 }
260
261 @Override
262 public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
263 if (element.equals(IconHandler.EL)) {
264 MutableIcon icon = new MutableIcon();
265 getInstance().add(icon);
266 new IconHandler(icon, this);
267 }
268 }
269
270 @Override
271 public boolean isLastElement(ELEMENT element) {
272 return element.equals(EL);
273 }
274 }
275
276 protected static class IconHandler extends DeviceDescriptorHandler<MutableIcon> {
277
278 public static final ELEMENT EL = ELEMENT.icon;
279
280 public IconHandler(MutableIcon instance, DeviceDescriptorHandler parent) {
281 super(instance, parent);
282 }
283
284 @Override
285 public void endElement(ELEMENT element) throws SAXException {
286 switch (element) {
287 case width:
288 getInstance().width = Integer.valueOf(getCharacters());
289 break;
290 case height:
291 getInstance().height = Integer.valueOf(getCharacters());
292 break;
293 case depth:
294 try {
295 getInstance().depth = Integer.valueOf(getCharacters());
296 } catch(NumberFormatException ex) {
297 log.warning("Invalid icon depth '" + getCharacters() + "', using 16 as default: " + ex);
298 getInstance().depth = 16;
299 }
300 break;
301 case url:
302 getInstance().uri = parseURI(getCharacters());
303 break;
304 case mimetype:
305 try {
306 getInstance().mimeType = getCharacters();
307 MimeType.valueOf(getInstance().mimeType);
308 } catch(IllegalArgumentException ex) {
309 log.warning("Ignoring invalid icon mime type: " + getInstance().mimeType);
310 getInstance().mimeType = "";
311 }
312 break;
313 }
314 }
315
316 @Override
317 public boolean isLastElement(ELEMENT element) {
318 return element.equals(EL);
319 }
320 }
321
322 protected static class ServiceListHandler extends DeviceDescriptorHandler<List<MutableService>> {
323
324 public static final ELEMENT EL = ELEMENT.serviceList;
325
326 public ServiceListHandler(List<MutableService> instance, DeviceDescriptorHandler parent) {
327 super(instance, parent);
328 }
329
330 @Override
331 public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
332 if (element.equals(ServiceHandler.EL)) {
333 MutableService service = new MutableService();
334 getInstance().add(service);
335 new ServiceHandler(service, this);
336 }
337 }
338
339 @Override
340 public boolean isLastElement(ELEMENT element) {
341 boolean last = element.equals(EL);
342 if (last) {
343 Iterator<MutableService> it = getInstance().iterator();
344 while (it.hasNext()) {
345 MutableService service = it.next();
346 if (service.serviceType == null || service.serviceId == null)
347 it.remove();
348 }
349 }
350 return last;
351 }
352 }
353
354 protected static class ServiceHandler extends DeviceDescriptorHandler<MutableService> {
355
356 public static final ELEMENT EL = ELEMENT.service;
357
358 public ServiceHandler(MutableService instance, DeviceDescriptorHandler parent) {
359 super(instance, parent);
360 }
361
362 @Override
363 public void endElement(ELEMENT element) throws SAXException {
364 try {
365 switch (element) {
366 case serviceType:
367 getInstance().serviceType = ServiceType.valueOf(getCharacters());
368 break;
369 case serviceId:
370 getInstance().serviceId = ServiceId.valueOf(getCharacters());
371 break;
372 case SCPDURL:
373 getInstance().descriptorURI = parseURI(getCharacters());
374 break;
375 case controlURL:
376 getInstance().controlURI = parseURI(getCharacters());
377 break;
378 case eventSubURL:
379 getInstance().eventSubscriptionURI = parseURI(getCharacters());
380 break;
381 }
382 } catch (InvalidValueException ex) {
383 log.warning(
384 "UPnP specification violation, skipping invalid service declaration. " + ex.getMessage()
385 );
386 }
387 }
388
389 @Override
390 public boolean isLastElement(ELEMENT element) {
391 return element.equals(EL);
392 }
393 }
394
395 protected static class DeviceListHandler extends DeviceDescriptorHandler<List<MutableDevice>> {
396
397 public static final ELEMENT EL = ELEMENT.deviceList;
398
399 public DeviceListHandler(List<MutableDevice> instance, DeviceDescriptorHandler parent) {
400 super(instance, parent);
401 }
402
403 @Override
404 public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
405 if (element.equals(DeviceHandler.EL)) {
406 MutableDevice device = new MutableDevice();
407 getInstance().add(device);
408 new DeviceHandler(device, this);
409 }
410 }
411
412 @Override
413 public boolean isLastElement(ELEMENT element) {
414 return element.equals(EL);
415 }
416 }
417
418 protected static class DeviceDescriptorHandler<I> extends SAXParser.Handler<I> {
419
420 public DeviceDescriptorHandler(I instance) {
421 super(instance);
422 }
423
424 public DeviceDescriptorHandler(I instance, SAXParser parser) {
425 super(instance, parser);
426 }
427
428 public DeviceDescriptorHandler(I instance, DeviceDescriptorHandler parent) {
429 super(instance, parent);
430 }
431
432 public DeviceDescriptorHandler(I instance, SAXParser parser, DeviceDescriptorHandler parent) {
433 super(instance, parser, parent);
434 }
435
436 @Override
437 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
438 super.startElement(uri, localName, qName, attributes);
439 ELEMENT el = ELEMENT.valueOrNullOf(localName);
440 if (el == null) return;
441 startElement(el, attributes);
442 }
443
444 @Override
445 public void endElement(String uri, String localName, String qName) throws SAXException {
446 super.endElement(uri, localName, qName);
447 ELEMENT el = ELEMENT.valueOrNullOf(localName);
448 if (el == null) return;
449 endElement(el);
450 }
451
452 @Override
453 protected boolean isLastElement(String uri, String localName, String qName) {
454 ELEMENT el = ELEMENT.valueOrNullOf(localName);
455 return el != null && isLastElement(el);
456 }
457
458 public void startElement(ELEMENT element, Attributes attributes) throws SAXException {
459
460 }
461
462 public void endElement(ELEMENT element) throws SAXException {
463
464 }
465
466 public boolean isLastElement(ELEMENT element) {
467 return false;
468 }
469 }
470 }