1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.fourthline.cling.support.contentdirectory;
17
18 import org.fourthline.cling.model.types.Datatype;
19 import org.fourthline.cling.model.types.InvalidValueException;
20 import org.fourthline.cling.support.model.DIDLAttribute;
21 import org.fourthline.cling.support.model.DIDLContent;
22 import org.fourthline.cling.support.model.DIDLObject;
23 import org.fourthline.cling.support.model.DescMeta;
24 import org.fourthline.cling.support.model.Person;
25 import org.fourthline.cling.support.model.PersonWithRole;
26 import org.fourthline.cling.support.model.ProtocolInfo;
27 import org.fourthline.cling.support.model.Res;
28 import org.fourthline.cling.support.model.StorageMedium;
29 import org.fourthline.cling.support.model.WriteStatus;
30 import org.fourthline.cling.support.model.container.Container;
31 import org.fourthline.cling.support.model.item.Item;
32 import org.seamless.util.io.IO;
33 import org.seamless.util.Exceptions;
34 import org.seamless.xml.SAXParser;
35 import org.w3c.dom.Document;
36 import org.w3c.dom.Element;
37 import org.w3c.dom.Node;
38 import org.w3c.dom.NodeList;
39 import org.xml.sax.Attributes;
40 import org.xml.sax.InputSource;
41 import org.xml.sax.SAXException;
42
43 import javax.xml.parsers.DocumentBuilderFactory;
44 import javax.xml.transform.OutputKeys;
45 import javax.xml.transform.Transformer;
46 import javax.xml.transform.TransformerFactory;
47 import javax.xml.transform.dom.DOMSource;
48 import javax.xml.transform.stream.StreamResult;
49 import java.io.InputStream;
50 import java.io.StringReader;
51 import java.io.StringWriter;
52 import java.net.URI;
53 import java.util.logging.Level;
54 import java.util.logging.Logger;
55
56 import static org.fourthline.cling.model.XMLUtil.appendNewElement;
57 import static org.fourthline.cling.model.XMLUtil.appendNewElementIfNotNull;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 public class DIDLParser extends SAXParser {
79
80 final private static Logger log = Logger.getLogger(DIDLParser.class.getName());
81
82 public static final String UNKNOWN_TITLE = "Unknown Title";
83
84
85
86
87
88
89
90
91 public DIDLContent parseResource(String resource) throws Exception {
92 InputStream is = null;
93 try {
94 is = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
95 return parse(IO.readLines(is));
96 } finally {
97 if (is != null) is.close();
98 }
99 }
100
101
102
103
104
105
106
107
108 public DIDLContent parse(String xml) throws Exception {
109
110 if (xml == null || xml.length() == 0) {
111 throw new RuntimeException("Null or empty XML");
112 }
113
114 DIDLContent content = new DIDLContent();
115 createRootHandler(content, this);
116
117 log.fine("Parsing DIDL XML content");
118 parse(new InputSource(new StringReader(xml)));
119 return content;
120 }
121
122 protected RootHandler createRootHandler(DIDLContent instance, SAXParser parser) {
123 return new RootHandler(instance, parser);
124 }
125
126 protected ContainerHandler createContainerHandler(Container instance, Handler parent) {
127 return new ContainerHandler(instance, parent);
128 }
129
130 protected ItemHandler createItemHandler(Item instance, Handler parent) {
131 return new ItemHandler(instance, parent);
132 }
133
134 protected ResHandler createResHandler(Res instance, Handler parent) {
135 return new ResHandler(instance, parent);
136 }
137
138 protected DescMetaHandler createDescMetaHandler(DescMeta instance, Handler parent) {
139 return new DescMetaHandler(instance, parent);
140 }
141
142
143 protected Container createContainer(Attributes attributes) {
144 Container container = new Container();
145
146 container.setId(attributes.getValue("id"));
147 container.setParentID(attributes.getValue("parentID"));
148
149 if ((attributes.getValue("childCount") != null))
150 container.setChildCount(Integer.valueOf(attributes.getValue("childCount")));
151
152 try {
153 Boolean value = (Boolean) Datatype.Builtin.BOOLEAN.getDatatype().valueOf(
154 attributes.getValue("restricted")
155 );
156 if (value != null)
157 container.setRestricted(value);
158
159 value = (Boolean) Datatype.Builtin.BOOLEAN.getDatatype().valueOf(
160 attributes.getValue("searchable")
161 );
162 if (value != null)
163 container.setSearchable(value);
164 } catch (Exception ex) {
165
166 }
167
168 return container;
169 }
170
171 protected Item createItem(Attributes attributes) {
172 Item item = new Item();
173
174 item.setId(attributes.getValue("id"));
175 item.setParentID(attributes.getValue("parentID"));
176
177 try {
178 Boolean value = (Boolean)Datatype.Builtin.BOOLEAN.getDatatype().valueOf(
179 attributes.getValue("restricted")
180 );
181 if (value != null)
182 item.setRestricted(value);
183
184 } catch (Exception ex) {
185
186 }
187
188 if ((attributes.getValue("refID") != null))
189 item.setRefID(attributes.getValue("refID"));
190
191 return item;
192 }
193
194 protected Res createResource(Attributes attributes) {
195 Res res = new Res();
196
197 if (attributes.getValue("importUri") != null)
198 res.setImportUri(URI.create(attributes.getValue("importUri")));
199
200 try {
201 res.setProtocolInfo(
202 new ProtocolInfo(attributes.getValue("protocolInfo"))
203 );
204 } catch (InvalidValueException ex) {
205 log.warning("In DIDL content, invalid resource protocol info: " + Exceptions.unwrap(ex));
206 return null;
207 }
208
209 if (attributes.getValue("size") != null)
210 res.setSize(toLongOrNull(attributes.getValue("size")));
211
212 if (attributes.getValue("duration") != null)
213 res.setDuration(attributes.getValue("duration"));
214
215 if (attributes.getValue("bitrate") != null)
216 res.setBitrate(toLongOrNull(attributes.getValue("bitrate")));
217
218 if (attributes.getValue("sampleFrequency") != null)
219 res.setSampleFrequency(toLongOrNull(attributes.getValue("sampleFrequency")));
220
221 if (attributes.getValue("bitsPerSample") != null)
222 res.setBitsPerSample(toLongOrNull(attributes.getValue("bitsPerSample")));
223
224 if (attributes.getValue("nrAudioChannels") != null)
225 res.setNrAudioChannels(toLongOrNull(attributes.getValue("nrAudioChannels")));
226
227 if (attributes.getValue("colorDepth") != null)
228 res.setColorDepth(toLongOrNull(attributes.getValue("colorDepth")));
229
230 if (attributes.getValue("protection") != null)
231 res.setProtection(attributes.getValue("protection"));
232
233 if (attributes.getValue("resolution") != null)
234 res.setResolution(attributes.getValue("resolution"));
235
236 return res;
237 }
238
239 private Long toLongOrNull(String value) {
240 try {
241 return Long.valueOf(value);
242 } catch (NumberFormatException x) {
243 return null;
244 }
245 }
246
247 protected DescMeta createDescMeta(Attributes attributes) {
248 DescMeta desc = new DescMeta();
249
250 desc.setId(attributes.getValue("id"));
251
252 if ((attributes.getValue("type") != null))
253 desc.setType(attributes.getValue("type"));
254
255 if ((attributes.getValue("nameSpace") != null))
256 desc.setNameSpace(URI.create(attributes.getValue("nameSpace")));
257
258 return desc;
259 }
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276 public String generate(DIDLContent content) throws Exception {
277 return generate(content, false);
278 }
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294 public String generate(DIDLContent content, boolean nestedItems) throws Exception {
295 return documentToString(buildDOM(content, nestedItems), true);
296 }
297
298
299
300 protected String documentToString(Document document, boolean omitProlog) throws Exception {
301 TransformerFactory transFactory = TransformerFactory.newInstance();
302
303
304
305
306 Transformer transformer = transFactory.newTransformer();
307
308 if (omitProlog) {
309
310
311
312
313 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
314 }
315
316
317
318
319 StringWriter out = new StringWriter();
320 transformer.transform(new DOMSource(document), new StreamResult(out));
321 return out.toString();
322 }
323
324 protected Document buildDOM(DIDLContent content, boolean nestedItems) throws Exception {
325
326 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
327 factory.setNamespaceAware(true);
328
329 Document d = factory.newDocumentBuilder().newDocument();
330
331 generateRoot(content, d, nestedItems);
332
333 return d;
334 }
335
336 protected void generateRoot(DIDLContent content, Document descriptor, boolean nestedItems) {
337 Element rootElement = descriptor.createElementNS(DIDLContent.NAMESPACE_URI, "DIDL-Lite");
338 descriptor.appendChild(rootElement);
339
340
341 rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:upnp", DIDLObject.Property.UPNP.NAMESPACE.URI);
342 rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:dc", DIDLObject.Property.DC.NAMESPACE.URI);
343 rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:sec", DIDLObject.Property.SEC.NAMESPACE.URI);
344
345 for (Container container : content.getContainers()) {
346 if (container == null) continue;
347 generateContainer(container, descriptor, rootElement, nestedItems);
348 }
349
350 for (Item item : content.getItems()) {
351 if (item == null) continue;
352 generateItem(item, descriptor, rootElement);
353 }
354
355 for (DescMeta descMeta : content.getDescMetadata()) {
356 if (descMeta == null) continue;
357 generateDescMetadata(descMeta, descriptor, rootElement);
358 }
359 }
360
361 protected void generateContainer(Container container, Document descriptor, Element parent, boolean nestedItems) {
362
363 if (container.getClazz() == null) {
364 throw new RuntimeException("Missing 'upnp:class' element for container: " + container.getId());
365 }
366
367 Element containerElement = appendNewElement(descriptor, parent, "container");
368
369 if (container.getId() == null)
370 throw new NullPointerException("Missing id on container: " + container);
371 containerElement.setAttribute("id", container.getId());
372
373 if (container.getParentID() == null)
374 throw new NullPointerException("Missing parent id on container: " + container);
375 containerElement.setAttribute("parentID", container.getParentID());
376
377 if (container.getChildCount() != null) {
378 containerElement.setAttribute("childCount", Integer.toString(container.getChildCount()));
379 }
380
381 containerElement.setAttribute("restricted", booleanToInt(container.isRestricted()));
382 containerElement.setAttribute("searchable", booleanToInt(container.isSearchable()));
383
384 String title = container.getTitle();
385 if (title == null) {
386 log.warning("Missing 'dc:title' element for container: " + container.getId());
387 title = UNKNOWN_TITLE;
388 }
389
390 appendNewElementIfNotNull(
391 descriptor,
392 containerElement,
393 "dc:title",
394 title,
395 DIDLObject.Property.DC.NAMESPACE.URI
396 );
397
398 appendNewElementIfNotNull(
399 descriptor,
400 containerElement,
401 "dc:creator",
402 container.getCreator(),
403 DIDLObject.Property.DC.NAMESPACE.URI
404 );
405
406 appendNewElementIfNotNull(
407 descriptor,
408 containerElement,
409 "upnp:writeStatus",
410 container.getWriteStatus(),
411 DIDLObject.Property.UPNP.NAMESPACE.URI
412 );
413
414 appendClass(descriptor, containerElement, container.getClazz(), "upnp:class", false);
415
416 for (DIDLObject.Class searchClass : container.getSearchClasses()) {
417 appendClass(descriptor, containerElement, searchClass, "upnp:searchClass", true);
418 }
419
420 for (DIDLObject.Class createClass : container.getCreateClasses()) {
421 appendClass(descriptor, containerElement, createClass, "upnp:createClass", true);
422 }
423
424 appendProperties(descriptor, containerElement, container, "upnp", DIDLObject.Property.UPNP.NAMESPACE.class, DIDLObject.Property.UPNP.NAMESPACE.URI);
425 appendProperties(descriptor, containerElement, container, "dc", DIDLObject.Property.DC.NAMESPACE.class, DIDLObject.Property.DC.NAMESPACE.URI);
426
427 if (nestedItems) {
428 for (Item item : container.getItems()) {
429 if (item == null) continue;
430 generateItem(item, descriptor, containerElement);
431 }
432 }
433
434 for (Res resource : container.getResources()) {
435 if (resource == null) continue;
436 generateResource(resource, descriptor, containerElement);
437 }
438
439 for (DescMeta descMeta : container.getDescMetadata()) {
440 if (descMeta == null) continue;
441 generateDescMetadata(descMeta, descriptor, containerElement);
442 }
443 }
444
445 protected void generateItem(Item item, Document descriptor, Element parent) {
446
447 if (item.getClazz() == null) {
448 throw new RuntimeException("Missing 'upnp:class' element for item: " + item.getId());
449 }
450
451 Element itemElement = appendNewElement(descriptor, parent, "item");
452
453 if (item.getId() == null)
454 throw new NullPointerException("Missing id on item: " + item);
455 itemElement.setAttribute("id", item.getId());
456
457 if (item.getParentID() == null)
458 throw new NullPointerException("Missing parent id on item: " + item);
459 itemElement.setAttribute("parentID", item.getParentID());
460
461 if (item.getRefID() != null)
462 itemElement.setAttribute("refID", item.getRefID());
463 itemElement.setAttribute("restricted", booleanToInt(item.isRestricted()));
464
465 String title = item.getTitle();
466 if (title == null) {
467 log.warning("Missing 'dc:title' element for item: " + item.getId());
468 title = UNKNOWN_TITLE;
469 }
470
471 appendNewElementIfNotNull(
472 descriptor,
473 itemElement,
474 "dc:title",
475 title,
476 DIDLObject.Property.DC.NAMESPACE.URI
477 );
478
479 appendNewElementIfNotNull(
480 descriptor,
481 itemElement,
482 "dc:creator",
483 item.getCreator(),
484 DIDLObject.Property.DC.NAMESPACE.URI
485 );
486
487 appendNewElementIfNotNull(
488 descriptor,
489 itemElement,
490 "upnp:writeStatus",
491 item.getWriteStatus(),
492 DIDLObject.Property.UPNP.NAMESPACE.URI
493 );
494
495 appendClass(descriptor, itemElement, item.getClazz(), "upnp:class", false);
496
497 appendProperties(descriptor, itemElement, item, "upnp", DIDLObject.Property.UPNP.NAMESPACE.class, DIDLObject.Property.UPNP.NAMESPACE.URI);
498 appendProperties(descriptor, itemElement, item, "dc", DIDLObject.Property.DC.NAMESPACE.class, DIDLObject.Property.DC.NAMESPACE.URI);
499 appendProperties(descriptor, itemElement, item, "sec", DIDLObject.Property.SEC.NAMESPACE.class, DIDLObject.Property.SEC.NAMESPACE.URI);
500
501 for (Res resource : item.getResources()) {
502 if (resource == null) continue;
503 generateResource(resource, descriptor, itemElement);
504 }
505
506 for (DescMeta descMeta : item.getDescMetadata()) {
507 if (descMeta == null) continue;
508 generateDescMetadata(descMeta, descriptor, itemElement);
509 }
510 }
511
512 protected void generateResource(Res resource, Document descriptor, Element parent) {
513
514 if (resource.getValue() == null) {
515 throw new RuntimeException("Missing resource URI value" + resource);
516 }
517 if (resource.getProtocolInfo() == null) {
518 throw new RuntimeException("Missing resource protocol info: " + resource);
519 }
520
521 Element resourceElement = appendNewElement(descriptor, parent, "res", resource.getValue());
522 resourceElement.setAttribute("protocolInfo", resource.getProtocolInfo().toString());
523 if (resource.getImportUri() != null)
524 resourceElement.setAttribute("importUri", resource.getImportUri().toString());
525 if (resource.getSize() != null)
526 resourceElement.setAttribute("size", resource.getSize().toString());
527 if (resource.getDuration() != null)
528 resourceElement.setAttribute("duration", resource.getDuration());
529 if (resource.getBitrate() != null)
530 resourceElement.setAttribute("bitrate", resource.getBitrate().toString());
531 if (resource.getSampleFrequency() != null)
532 resourceElement.setAttribute("sampleFrequency", resource.getSampleFrequency().toString());
533 if (resource.getBitsPerSample() != null)
534 resourceElement.setAttribute("bitsPerSample", resource.getBitsPerSample().toString());
535 if (resource.getNrAudioChannels() != null)
536 resourceElement.setAttribute("nrAudioChannels", resource.getNrAudioChannels().toString());
537 if (resource.getColorDepth() != null)
538 resourceElement.setAttribute("colorDepth", resource.getColorDepth().toString());
539 if (resource.getProtection() != null)
540 resourceElement.setAttribute("protection", resource.getProtection());
541 if (resource.getResolution() != null)
542 resourceElement.setAttribute("resolution", resource.getResolution());
543 }
544
545 protected void generateDescMetadata(DescMeta descMeta, Document descriptor, Element parent) {
546
547 if (descMeta.getId() == null) {
548 throw new RuntimeException("Missing id of description metadata: " + descMeta);
549 }
550 if (descMeta.getNameSpace() == null) {
551 throw new RuntimeException("Missing namespace of description metadata: " + descMeta);
552 }
553
554 Element descElement = appendNewElement(descriptor, parent, "desc");
555 descElement.setAttribute("id", descMeta.getId());
556 descElement.setAttribute("nameSpace", descMeta.getNameSpace().toString());
557 if (descMeta.getType() != null)
558 descElement.setAttribute("type", descMeta.getType());
559 populateDescMetadata(descElement, descMeta);
560 }
561
562
563
564
565
566
567
568
569
570
571
572
573 protected void populateDescMetadata(Element descElement, DescMeta descMeta) {
574 if (descMeta.getMetadata() instanceof Document) {
575 Document doc = (Document) descMeta.getMetadata();
576
577 NodeList nl = doc.getDocumentElement().getChildNodes();
578 for (int i = 0; i < nl.getLength(); i++) {
579 Node n = nl.item(i);
580 if (n.getNodeType() != Node.ELEMENT_NODE)
581 continue;
582
583 Node clone = descElement.getOwnerDocument().importNode(n, true);
584 descElement.appendChild(clone);
585 }
586
587 } else {
588 log.warning("Unknown desc metadata content, please override populateDescMetadata(): " + descMeta.getMetadata());
589 }
590 }
591
592 protected void appendProperties(Document descriptor, Element parent, DIDLObject object, String prefix,
593 Class<? extends DIDLObject.Property.NAMESPACE> namespace,
594 String namespaceURI) {
595 for (DIDLObject.Property<Object> property : object.getPropertiesByNamespace(namespace)) {
596 Element el = descriptor.createElementNS(namespaceURI, prefix + ":" + property.getDescriptorName());
597 parent.appendChild(el);
598 property.setOnElement(el);
599 }
600 }
601
602 protected void appendClass(Document descriptor, Element parent, DIDLObject.Class clazz, String element, boolean appendDerivation) {
603 Element classElement = appendNewElementIfNotNull(
604 descriptor,
605 parent,
606 element,
607 clazz.getValue(),
608 DIDLObject.Property.UPNP.NAMESPACE.URI
609 );
610 if (clazz.getFriendlyName() != null && clazz.getFriendlyName().length() > 0)
611 classElement.setAttribute("name", clazz.getFriendlyName());
612 if (appendDerivation)
613 classElement.setAttribute("includeDerived", Boolean.toString(clazz.isIncludeDerived()));
614 }
615
616 protected String booleanToInt(boolean b) {
617 return b ? "1" : "0";
618 }
619
620
621
622
623
624
625 public void debugXML(String s) {
626 if (log.isLoggable(Level.FINE)) {
627 log.fine("-------------------------------------------------------------------------------------");
628 log.fine("\n" + s);
629 log.fine("-------------------------------------------------------------------------------------");
630 }
631 }
632
633
634
635
636
637 public abstract class DIDLObjectHandler<I extends DIDLObject> extends Handler<I> {
638
639 protected DIDLObjectHandler(I instance, Handler parent) {
640 super(instance, parent);
641 }
642
643 @Override
644 public void endElement(String uri, String localName, String qName) throws SAXException {
645 super.endElement(uri, localName, qName);
646
647 if (DIDLObject.Property.DC.NAMESPACE.URI.equals(uri)) {
648
649 if ("title".equals(localName)) {
650 getInstance().setTitle(getCharacters());
651 } else if ("creator".equals(localName)) {
652 getInstance().setCreator(getCharacters());
653 } else if ("description".equals(localName)) {
654 getInstance().addProperty(new DIDLObject.Property.DC.DESCRIPTION(getCharacters()));
655 } else if ("publisher".equals(localName)) {
656 getInstance().addProperty(new DIDLObject.Property.DC.PUBLISHER(new Person(getCharacters())));
657 } else if ("contributor".equals(localName)) {
658 getInstance().addProperty(new DIDLObject.Property.DC.CONTRIBUTOR(new Person(getCharacters())));
659 } else if ("date".equals(localName)) {
660 getInstance().addProperty(new DIDLObject.Property.DC.DATE(getCharacters()));
661 } else if ("language".equals(localName)) {
662 getInstance().addProperty(new DIDLObject.Property.DC.LANGUAGE(getCharacters()));
663 } else if ("rights".equals(localName)) {
664 getInstance().addProperty(new DIDLObject.Property.DC.RIGHTS(getCharacters()));
665 } else if ("relation".equals(localName)) {
666 getInstance().addProperty(new DIDLObject.Property.DC.RELATION(URI.create(getCharacters())));
667 }
668
669 } else if (DIDLObject.Property.UPNP.NAMESPACE.URI.equals(uri)) {
670
671 if ("writeStatus".equals(localName)) {
672 try {
673 getInstance().setWriteStatus(
674 WriteStatus.valueOf(getCharacters())
675 );
676 } catch (Exception ex) {
677 log.info("Ignoring invalid writeStatus value: " + getCharacters());
678 }
679 } else if ("class".equals(localName)) {
680 getInstance().setClazz(
681 new DIDLObject.Class(
682 getCharacters(),
683 getAttributes().getValue("name")
684 )
685 );
686 } else if ("artist".equals(localName)) {
687 getInstance().addProperty(
688 new DIDLObject.Property.UPNP.ARTIST(
689 new PersonWithRole(getCharacters(), getAttributes().getValue("role"))
690 )
691 );
692 } else if ("actor".equals(localName)) {
693 getInstance().addProperty(
694 new DIDLObject.Property.UPNP.ACTOR(
695 new PersonWithRole(getCharacters(), getAttributes().getValue("role"))
696 )
697 );
698 } else if ("author".equals(localName)) {
699 getInstance().addProperty(
700 new DIDLObject.Property.UPNP.AUTHOR(
701 new PersonWithRole(getCharacters(), getAttributes().getValue("role"))
702 )
703 );
704 } else if ("producer".equals(localName)) {
705 getInstance().addProperty(
706 new DIDLObject.Property.UPNP.PRODUCER(new Person(getCharacters()))
707 );
708 } else if ("director".equals(localName)) {
709 getInstance().addProperty(
710 new DIDLObject.Property.UPNP.DIRECTOR(new Person(getCharacters()))
711 );
712 } else if ("longDescription".equals(localName)) {
713 getInstance().addProperty(
714 new DIDLObject.Property.UPNP.LONG_DESCRIPTION(getCharacters())
715 );
716 } else if ("storageUsed".equals(localName)) {
717 getInstance().addProperty(
718 new DIDLObject.Property.UPNP.STORAGE_USED(Long.valueOf(getCharacters()))
719 );
720 } else if ("storageTotal".equals(localName)) {
721 getInstance().addProperty(
722 new DIDLObject.Property.UPNP.STORAGE_TOTAL(Long.valueOf(getCharacters()))
723 );
724 } else if ("storageFree".equals(localName)) {
725 getInstance().addProperty(
726 new DIDLObject.Property.UPNP.STORAGE_FREE(Long.valueOf(getCharacters()))
727 );
728 } else if ("storageMaxPartition".equals(localName)) {
729 getInstance().addProperty(
730 new DIDLObject.Property.UPNP.STORAGE_MAX_PARTITION(Long.valueOf(getCharacters()))
731 );
732 } else if ("storageMedium".equals(localName)) {
733 getInstance().addProperty(
734 new DIDLObject.Property.UPNP.STORAGE_MEDIUM(StorageMedium.valueOrVendorSpecificOf(getCharacters()))
735 );
736 } else if ("genre".equals(localName)) {
737 getInstance().addProperty(
738 new DIDLObject.Property.UPNP.GENRE(getCharacters())
739 );
740 } else if ("album".equals(localName)) {
741 getInstance().addProperty(
742 new DIDLObject.Property.UPNP.ALBUM(getCharacters())
743 );
744 } else if ("playlist".equals(localName)) {
745 getInstance().addProperty(
746 new DIDLObject.Property.UPNP.PLAYLIST(getCharacters())
747 );
748 } else if ("region".equals(localName)) {
749 getInstance().addProperty(
750 new DIDLObject.Property.UPNP.REGION(getCharacters())
751 );
752 } else if ("rating".equals(localName)) {
753 getInstance().addProperty(
754 new DIDLObject.Property.UPNP.RATING(getCharacters())
755 );
756 } else if ("toc".equals(localName)) {
757 getInstance().addProperty(
758 new DIDLObject.Property.UPNP.TOC(getCharacters())
759 );
760 } else if ("albumArtURI".equals(localName)) {
761 DIDLObject.Property albumArtURI = new DIDLObject.Property.UPNP.ALBUM_ART_URI(URI.create(getCharacters()));
762
763 Attributes albumArtURIAttributes = getAttributes();
764 for (int i = 0; i < albumArtURIAttributes.getLength(); i++) {
765 if ("profileID".equals(albumArtURIAttributes.getLocalName(i))) {
766 albumArtURI.addAttribute(
767 new DIDLObject.Property.DLNA.PROFILE_ID(
768 new DIDLAttribute(
769 DIDLObject.Property.DLNA.NAMESPACE.URI,
770 "dlna",
771 albumArtURIAttributes.getValue(i))
772 ));
773 }
774 }
775
776 getInstance().addProperty(albumArtURI);
777 } else if ("artistDiscographyURI".equals(localName)) {
778 getInstance().addProperty(
779 new DIDLObject.Property.UPNP.ARTIST_DISCO_URI(URI.create(getCharacters()))
780 );
781 } else if ("lyricsURI".equals(localName)) {
782 getInstance().addProperty(
783 new DIDLObject.Property.UPNP.LYRICS_URI(URI.create(getCharacters()))
784 );
785 } else if ("icon".equals(localName)) {
786 getInstance().addProperty(
787 new DIDLObject.Property.UPNP.ICON(URI.create(getCharacters()))
788 );
789 } else if ("radioCallSign".equals(localName)) {
790 getInstance().addProperty(
791 new DIDLObject.Property.UPNP.RADIO_CALL_SIGN(getCharacters())
792 );
793 } else if ("radioStationID".equals(localName)) {
794 getInstance().addProperty(
795 new DIDLObject.Property.UPNP.RADIO_STATION_ID(getCharacters())
796 );
797 } else if ("radioBand".equals(localName)) {
798 getInstance().addProperty(
799 new DIDLObject.Property.UPNP.RADIO_BAND(getCharacters())
800 );
801 } else if ("channelNr".equals(localName)) {
802 getInstance().addProperty(
803 new DIDLObject.Property.UPNP.CHANNEL_NR(Integer.valueOf(getCharacters()))
804 );
805 } else if ("channelName".equals(localName)) {
806 getInstance().addProperty(
807 new DIDLObject.Property.UPNP.CHANNEL_NAME(getCharacters())
808 );
809 } else if ("scheduledStartTime".equals(localName)) {
810 getInstance().addProperty(
811 new DIDLObject.Property.UPNP.SCHEDULED_START_TIME(getCharacters())
812 );
813 } else if ("scheduledEndTime".equals(localName)) {
814 getInstance().addProperty(
815 new DIDLObject.Property.UPNP.SCHEDULED_END_TIME(getCharacters())
816 );
817 } else if ("DVDRegionCode".equals(localName)) {
818 getInstance().addProperty(
819 new DIDLObject.Property.UPNP.DVD_REGION_CODE(Integer.valueOf(getCharacters()))
820 );
821 } else if ("originalTrackNumber".equals(localName)) {
822 getInstance().addProperty(
823 new DIDLObject.Property.UPNP.ORIGINAL_TRACK_NUMBER(Integer.valueOf(getCharacters()))
824 );
825 } else if ("userAnnotation".equals(localName)) {
826 getInstance().addProperty(
827 new DIDLObject.Property.UPNP.USER_ANNOTATION(getCharacters())
828 );
829 }
830 }
831 }
832 }
833
834 public class RootHandler extends Handler<DIDLContent> {
835
836 RootHandler(DIDLContent instance, SAXParser parser) {
837 super(instance, parser);
838 }
839
840 @Override
841 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
842 super.startElement(uri, localName, qName, attributes);
843
844 if (!DIDLContent.NAMESPACE_URI.equals(uri)) return;
845
846 if (localName.equals("container")) {
847
848 Container container = createContainer(attributes);
849 getInstance().addContainer(container);
850 createContainerHandler(container, this);
851
852 } else if (localName.equals("item")) {
853
854 Item item = createItem(attributes);
855 getInstance().addItem(item);
856 createItemHandler(item, this);
857
858 } else if (localName.equals("desc")) {
859
860 DescMeta desc = createDescMeta(attributes);
861 getInstance().addDescMetadata(desc);
862 createDescMetaHandler(desc, this);
863
864 }
865 }
866
867 @Override
868 protected boolean isLastElement(String uri, String localName, String qName) {
869 if (DIDLContent.NAMESPACE_URI.equals(uri) && "DIDL-Lite".equals(localName)) {
870
871
872
873 getInstance().replaceGenericContainerAndItems();
874
875 return true;
876 }
877 return false;
878 }
879 }
880
881 public class ContainerHandler extends DIDLObjectHandler<Container> {
882 public ContainerHandler(Container instance, Handler parent) {
883 super(instance, parent);
884 }
885
886 @Override
887 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
888 super.startElement(uri, localName, qName, attributes);
889
890 if (!DIDLContent.NAMESPACE_URI.equals(uri)) return;
891
892 if (localName.equals("item")) {
893
894 Item item = createItem(attributes);
895 getInstance().addItem(item);
896 createItemHandler(item, this);
897
898 } else if (localName.equals("desc")) {
899
900 DescMeta desc = createDescMeta(attributes);
901 getInstance().addDescMetadata(desc);
902 createDescMetaHandler(desc, this);
903
904 } else if (localName.equals("res")) {
905
906 Res res = createResource(attributes);
907 if (res != null) {
908 getInstance().addResource(res);
909 createResHandler(res, this);
910 }
911
912 }
913
914
915
916
917
918
919
920 }
921
922 @Override
923 public void endElement(String uri, String localName, String qName) throws SAXException {
924 super.endElement(uri, localName, qName);
925
926 if (DIDLObject.Property.UPNP.NAMESPACE.URI.equals(uri)) {
927
928 if ("searchClass".equals(localName)) {
929 getInstance().getSearchClasses().add(
930 new DIDLObject.Class(
931 getCharacters(),
932 getAttributes().getValue("name"),
933 "true".equals(getAttributes().getValue("includeDerived"))
934 )
935 );
936 } else if ("createClass".equals(localName)) {
937 getInstance().getCreateClasses().add(
938 new DIDLObject.Class(
939 getCharacters(),
940 getAttributes().getValue("name"),
941 "true".equals(getAttributes().getValue("includeDerived"))
942 )
943 );
944 }
945 }
946 }
947
948 @Override
949 protected boolean isLastElement(String uri, String localName, String qName) {
950 if (DIDLContent.NAMESPACE_URI.equals(uri) && "container".equals(localName)) {
951 if (getInstance().getTitle() == null) {
952 log.warning("In DIDL content, missing 'dc:title' element for container: " + getInstance().getId());
953 }
954 if (getInstance().getClazz() == null) {
955 log.warning("In DIDL content, missing 'upnp:class' element for container: " + getInstance().getId());
956 }
957 return true;
958 }
959 return false;
960 }
961 }
962
963 public class ItemHandler extends DIDLObjectHandler<Item> {
964 public ItemHandler(Item instance, Handler parent) {
965 super(instance, parent);
966 }
967
968 @Override
969 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
970 super.startElement(uri, localName, qName, attributes);
971
972 if (!DIDLContent.NAMESPACE_URI.equals(uri)) return;
973
974 if (localName.equals("res")) {
975
976 Res res = createResource(attributes);
977 if (res != null) {
978 getInstance().addResource(res);
979 createResHandler(res, this);
980 }
981
982 } else if (localName.equals("desc")) {
983
984 DescMeta desc = createDescMeta(attributes);
985 getInstance().addDescMetadata(desc);
986 createDescMetaHandler(desc, this);
987
988 }
989 }
990
991 @Override
992 protected boolean isLastElement(String uri, String localName, String qName) {
993 if (DIDLContent.NAMESPACE_URI.equals(uri) && "item".equals(localName)) {
994 if (getInstance().getTitle() == null) {
995 log.warning("In DIDL content, missing 'dc:title' element for item: " + getInstance().getId());
996 }
997 if (getInstance().getClazz() == null) {
998 log.warning("In DIDL content, missing 'upnp:class' element for item: " + getInstance().getId());
999 }
1000 return true;
1001 }
1002 return false;
1003 }
1004 }
1005
1006 protected class ResHandler extends Handler<Res> {
1007 public ResHandler(Res instance, Handler parent) {
1008 super(instance, parent);
1009 }
1010
1011 @Override
1012 public void endElement(String uri, String localName, String qName) throws SAXException {
1013 super.endElement(uri, localName, qName);
1014 getInstance().setValue(getCharacters());
1015 }
1016
1017 @Override
1018 protected boolean isLastElement(String uri, String localName, String qName) {
1019 return DIDLContent.NAMESPACE_URI.equals(uri) && "res".equals(localName);
1020 }
1021 }
1022
1023
1024
1025
1026
1027
1028
1029
1030 public class DescMetaHandler extends Handler<DescMeta> {
1031
1032 protected Element current;
1033
1034 public DescMetaHandler(DescMeta instance, Handler parent) {
1035 super(instance, parent);
1036 instance.setMetadata(instance.createMetadataDocument());
1037 current = getInstance().getMetadata().getDocumentElement();
1038 }
1039
1040 @Override
1041 public DescMeta<Document> getInstance() {
1042 return super.getInstance();
1043 }
1044
1045 @Override
1046 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
1047 super.startElement(uri, localName, qName, attributes);
1048
1049 Element newEl = getInstance().getMetadata().createElementNS(uri, qName);
1050 for (int i = 0; i < attributes.getLength(); i++) {
1051 newEl.setAttributeNS(
1052 attributes.getURI(i),
1053 attributes.getQName(i),
1054 attributes.getValue(i)
1055 );
1056 }
1057 current.appendChild(newEl);
1058 current = newEl;
1059 }
1060
1061 @Override
1062 public void endElement(String uri, String localName, String qName) throws SAXException {
1063 super.endElement(uri, localName, qName);
1064 if (isLastElement(uri, localName, qName)) return;
1065
1066
1067 if (getCharacters().length() > 0 && !getCharacters().matches("[\\t\\n\\x0B\\f\\r\\s]+"))
1068 current.appendChild(getInstance().getMetadata().createTextNode(getCharacters()));
1069
1070 current = (Element) current.getParentNode();
1071
1072
1073 characters = new StringBuilder();
1074 attributes = null;
1075 }
1076
1077 @Override
1078 protected boolean isLastElement(String uri, String localName, String qName) {
1079 return DIDLContent.NAMESPACE_URI.equals(uri) && "desc".equals(localName);
1080 }
1081 }
1082 }