📄 didlparser.java
字号:
/* * Copyright (C) 2011 Teleal GmbH, Switzerland * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */package org.teleal.cling.support.contentdirectory;import org.teleal.cling.model.types.Datatype;import org.teleal.cling.model.types.InvalidValueException;import org.teleal.cling.support.model.DIDLAttribute;import org.teleal.cling.support.model.DIDLContent;import org.teleal.cling.support.model.DIDLObject;import org.teleal.cling.support.model.DescMeta;import org.teleal.cling.support.model.Person;import org.teleal.cling.support.model.PersonWithRole;import org.teleal.cling.support.model.ProtocolInfo;import org.teleal.cling.support.model.Res;import org.teleal.cling.support.model.StorageMedium;import org.teleal.cling.support.model.WriteStatus;import org.teleal.cling.support.model.container.Container;import org.teleal.cling.support.model.item.Item;import org.teleal.common.io.IO;import org.teleal.common.util.Exceptions;import org.teleal.common.xml.SAXParser;import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import org.xml.sax.Attributes;import org.xml.sax.InputSource;import org.xml.sax.SAXException;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.transform.OutputKeys;import javax.xml.transform.Transformer;import javax.xml.transform.TransformerFactory;import javax.xml.transform.dom.DOMSource;import javax.xml.transform.stream.StreamResult;import java.io.InputStream;import java.io.StringReader;import java.io.StringWriter;import java.net.URI;import java.util.logging.Level;import java.util.logging.Logger;import static org.teleal.cling.model.XMLUtil.appendNewElement;import static org.teleal.cling.model.XMLUtil.appendNewElementIfNotNull;/** * DIDL parser based on SAX for reading and DOM for writing. * <p> * This parser requires Android platform level 8 (2.2). * </p> * <p> * Override the {@link #createDescMetaHandler(org.teleal.cling.support.model.DescMeta, org.teleal.common.xml.SAXParser.Handler)} * method to read vendor extension content of {@code <desc>} elements. You then should also override the * {@link #populateDescMetadata(org.w3c.dom.Element, org.teleal.cling.support.model.DescMeta)} method for writing. * </p> * <p> * Override the {@link #createItemHandler(org.teleal.cling.support.model.item.Item, org.teleal.common.xml.SAXParser.Handler)} * etc. methods to register custom handlers for vendor-specific elements and attributes within items, containers, * and so on. * </p> * * @author Christian Bauer * @author Mario Franco */public class DIDLParser extends SAXParser { final private static Logger log = Logger.getLogger(DIDLParser.class.getName()); /** * Uses the current thread's context classloader to read and unmarshall the given resource. * * @param resource The resource on the classpath. * @return The unmarshalled DIDL content model. * @throws Exception */ public DIDLContent parseResource(String resource) throws Exception { InputStream is = null; try { is = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource); return parse(IO.readLines(is)); } finally { if (is != null) is.close(); } } /** * Reads and unmarshalls an XML representation into a DIDL content model. * * @param xml The XML representation. * @return A DIDL content model. * @throws Exception */ public DIDLContent parse(String xml) throws Exception { if (xml == null || xml.length() == 0) { throw new RuntimeException("Null or empty XML"); } DIDLContent content = new DIDLContent(); createRootHandler(content, this); log.fine("Parsing DIDL XML content"); parse(new InputSource(new StringReader(xml))); return content; } protected RootHandler createRootHandler(DIDLContent instance, SAXParser parser) { return new RootHandler(instance, parser); } protected ContainerHandler createContainerHandler(Container instance, Handler parent) { return new ContainerHandler(instance, parent); } protected ItemHandler createItemHandler(Item instance, Handler parent) { return new ItemHandler(instance, parent); } protected ResHandler createResHandler(Res instance, Handler parent) { return new ResHandler(instance, parent); } protected DescMetaHandler createDescMetaHandler(DescMeta instance, Handler parent) { return new DescMetaHandler(instance, parent); } protected Container createContainer(Attributes attributes) { Container container = new Container(); container.setId(attributes.getValue("id")); container.setParentID(attributes.getValue("parentID")); if ((attributes.getValue("childCount") != null)) container.setChildCount(Integer.valueOf(attributes.getValue("childCount"))); try { Boolean value = (Boolean)Datatype.Builtin.BOOLEAN.getDatatype().valueOf( attributes.getValue("restricted") ); if (value != null) container.setRestricted(value); value = (Boolean)Datatype.Builtin.BOOLEAN.getDatatype().valueOf( attributes.getValue("searchable") ); if (value != null) container.setSearchable(value); } catch (Exception ex) { // Ignore } return container; } protected Item createItem(Attributes attributes) { Item item = new Item(); item.setId(attributes.getValue("id")); item.setParentID(attributes.getValue("parentID")); try { Boolean value = (Boolean)Datatype.Builtin.BOOLEAN.getDatatype().valueOf( attributes.getValue("restricted") ); if (value != null) item.setRestricted(value); } catch (Exception ex) { // Ignore } if ((attributes.getValue("refID") != null)) item.setRefID(attributes.getValue("refID")); return item; } protected Res createResource(Attributes attributes) { Res res = new Res(); if (attributes.getValue("importUri") != null) res.setImportUri(URI.create(attributes.getValue("importUri"))); try { res.setProtocolInfo( new ProtocolInfo(attributes.getValue("protocolInfo")) ); } catch (InvalidValueException ex) { log.warning("In DIDL content, invalid resource protocol info: " + Exceptions.unwrap(ex)); return null; } if (attributes.getValue("size") != null) res.setSize(Long.valueOf(attributes.getValue("size"))); if (attributes.getValue("duration") != null) res.setDuration(attributes.getValue("duration")); if (attributes.getValue("bitrate") != null) res.setBitrate(Long.valueOf(attributes.getValue("bitrate"))); if (attributes.getValue("sampleFrequency") != null) res.setSampleFrequency(Long.valueOf(attributes.getValue("sampleFrequency"))); if (attributes.getValue("bitsPerSample") != null) res.setBitsPerSample(Long.valueOf(attributes.getValue("bitsPerSample"))); if (attributes.getValue("nrAudioChannels") != null) res.setNrAudioChannels(Long.valueOf(attributes.getValue("nrAudioChannels"))); if (attributes.getValue("colorDepth") != null) res.setColorDepth(Long.valueOf(attributes.getValue("colorDepth"))); if (attributes.getValue("protection") != null) res.setProtection(attributes.getValue("protection")); if (attributes.getValue("resolution") != null) res.setResolution(attributes.getValue("resolution")); return res; } protected DescMeta createDescMeta(Attributes attributes) { DescMeta desc = new DescMeta(); desc.setId(attributes.getValue("id")); if ((attributes.getValue("type") != null)) desc.setType(attributes.getValue("type")); if ((attributes.getValue("nameSpace") != null)) desc.setNameSpace(URI.create(attributes.getValue("nameSpace"))); return desc; } /* ############################################################################################# */ /** * Generates a XML representation of the content model. * <p> * Items inside a container will <em>not</em> be represented in the XML, the containers * will be rendered flat without children. * </p> * * @param content The content model. * @return An XML representation. * @throws Exception */ public String generate(DIDLContent content) throws Exception { return generate(content, false); } /** * Generates an XML representation of the content model. * <p> * Optionally, items inside a container will be represented in the XML, * the container elements then have nested item elements. Although this * parser can read such a structure, it is unclear whether other DIDL * parsers should and actually do support this XML. * </p> * * @param content The content model. * @param nestedItems <code>true</code> if nested item elements should be rendered for containers. * @return An XML representation. * @throws Exception */ public String generate(DIDLContent content, boolean nestedItems) throws Exception { return documentToString(buildDOM(content, nestedItems), true); } // TODO: Yes, this only runs on Android 2.2 protected String documentToString(Document document, boolean omitProlog) throws Exception { TransformerFactory transFactory = TransformerFactory.newInstance(); // Indentation not supported on Android 2.2 //transFactory.setAttribute("indent-number", 4); Transformer transformer = transFactory.newTransformer(); if (omitProlog) { // TODO: UPNP VIOLATION: Terratec Noxon Webradio fails when DIDL content has a prolog // No XML prolog! This is allowed because it is UTF-8 encoded and required // because broken devices will stumble on SOAP messages that contain (even // encoded) XML prologs within a message body. transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); } // Again, Android 2.2 fails hard if you try this. //transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter out = new StringWriter(); transformer.transform(new DOMSource(document), new StreamResult(out)); return out.toString(); } protected Document buildDOM(DIDLContent content, boolean nestedItems) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); Document d = factory.newDocumentBuilder().newDocument(); generateRoot(content, d, nestedItems); return d; } protected void generateRoot(DIDLContent content, Document descriptor, boolean nestedItems) { Element rootElement = descriptor.createElementNS(DIDLContent.NAMESPACE_URI, "DIDL-Lite"); descriptor.appendChild(rootElement); // rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:didl", DIDLContent.NAMESPACE_URI); rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:upnp", DIDLObject.Property.UPNP.NAMESPACE.URI); rootElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:dc", DIDLObject.Property.DC.NAMESPACE.URI); for (Container container : content.getContainers()) { if (container == null) continue; generateContainer(container, descriptor, rootElement, nestedItems); } for (Item item : content.getItems()) { if (item == null) continue; generateItem(item, descriptor, rootElement); } for (DescMeta descMeta : content.getDescMetadata()) { if (descMeta == null) continue; generateDescMetadata(descMeta, descriptor, rootElement); } } protected void generateContainer(Container container, Document descriptor, Element parent, boolean nestedItems) { if (container.getTitle() == null) { throw new RuntimeException("Missing 'dc:title' element for container: " + container.getId());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -