/* * ==================================================================== * ======== The Apache Software License, Version 1.1 * ================== * ========================================================== * Copyright (C) 2002 The Apache Software Foundation. All rights * reserved. Redistribution and use in source and binary forms, with * or without modifica- tion, are permitted provided that the * following conditions are met: 1. Redistributions of source code * must retain the above copyright notice, this list of conditions and * the following disclaimer. 2. Redistributions in binary form must * reproduce the above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or other * materials provided with the distribution. 3. The end-user * documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes * software developed by SuperBonBon Industries * (http://www.sbbi.net/)." Alternately, this acknowledgment may * appear in the software itself, if and wherever such third-party * acknowledgments normally appear. 4. The names "UPNPLib" and * "SuperBonBon Industries" must not be used to endorse or promote * products derived from this software without prior written * permission. For written permission, please contact info@sbbi.net. * 5. Products derived from this software may not be called * "SuperBonBon Industries", nor may "SBBI" appear in their name, * without prior written permission of SuperBonBon Industries. THIS * SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- DING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. This software consists of voluntary contributions made * by many individuals on behalf of SuperBonBon Industries. For more * information on SuperBonBon Industries, please see * <http://www.sbbi.net/>. */ package net.tomp2p.upnp; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import org.w3c.dom.Node; /** * This class represents an UPNP device, this device contains a set of services * that will be needed to access the device functionalities. * * @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a> * @version 1.0 */ public class Device { /***/ public final String deviceType; /***/ public final String friendlyName; /***/ public final String manufacturer; /***/ public final URL manufacturerURL; /***/ public final URL presentationURL; /***/ public final String modelDescription; /***/ public final String modelName; /***/ public final String modelNumber; /***/ public final String modelURL; /***/ public final String serialNumber; /***/ public final String UDN; /***/ public final String USN; /***/ public final long UPC; /***/ public final Service[] services; /***/ public final Device[] childDevices; /***/ public final Device parent; /** * @param deviceCtx * @param parent * @param urlBase */ public Device(Node deviceCtx, Device parent, URL urlBase) { deviceType = getMandatoryData(deviceCtx, "deviceType"); friendlyName = getMandatoryData(deviceCtx, "friendlyName"); manufacturer = getNonMandatoryData(deviceCtx, "manufacturer"); URL url = null; try { url = new URL(getNonMandatoryData(deviceCtx, "manufacturerURL")); } catch (java.net.MalformedURLException ex) { // crappy data provided, keep the field null } manufacturerURL = url; url = null; try { url = getURL(getNonMandatoryData(deviceCtx, "presentationURL"), urlBase); } catch (java.net.MalformedURLException ex) { // crappy data provided, keep the field null } presentationURL = url; modelDescription = getNonMandatoryData(deviceCtx, "modelDescription"); modelName = getMandatoryData(deviceCtx, "modelName"); modelNumber = getNonMandatoryData(deviceCtx, "modelNumber"); modelURL = getNonMandatoryData(deviceCtx, "modelURL"); serialNumber = getNonMandatoryData(deviceCtx, "serialNumber"); UDN = getMandatoryData(deviceCtx, "UDN"); USN = UDN.concat("::").concat(deviceType); String tmp = getNonMandatoryData(deviceCtx, "UPC"); long l = -1; if (tmp != null) { try { l = Long.parseLong(tmp); } catch (Exception ex) { // non all numeric field provided, non upnp compliant // device } } UPC = l; this.parent = parent; // service list List<Service> sList = new ArrayList<Service>(); try { Node serviceList = (Node) XMLUtil.xpath.evaluate("serviceList", deviceCtx, XPathConstants.NODE); int serviceCount = Integer.parseInt(XMLUtil.xpath.evaluate("count( service )", serviceList)); for (int i = 1; i <= serviceCount; i++) { Node serviceXML = (Node) XMLUtil.xpath.evaluate("service[" + i + "]", serviceList, XPathConstants.NODE); try { sList.add(new Service(serviceXML, urlBase, this)); } catch (MalformedURLException e) { e.printStackTrace(); } } } catch (XPathExpressionException e) { e.printStackTrace(); } services = sList.toArray(new Service[sList.size()]); // child devices List<Device> children = new ArrayList<Device>(); try { Node devList = (Node) XMLUtil.xpath.evaluate("deviceList", deviceCtx, XPathConstants.NODE); int devCount = Integer.parseInt(XMLUtil.xpath.evaluate("count( device )", devList)); for (int i = 1; i <= devCount; i++) { Node devXML = (Node) XMLUtil.xpath.evaluate("device[" + i + "]", devList, XPathConstants.NODE); Device d = new Device(devXML, this, urlBase); children.add(d); } } catch (XPathExpressionException e) { // no sub-devices } childDevices = children.toArray(new Device[children.size()]); } private String getNonMandatoryData(Node ctx, String ctxFieldName) { String value = null; try { value = XMLUtil.xpath.evaluate(ctxFieldName, ctx); } catch (XPathExpressionException e) { e.printStackTrace(); } if (value != null && value.length() == 0) { value = null; } return value; } private static String getMandatoryData(Node ctx, String ctxFieldName) { String value = null; try { value = XMLUtil.xpath.evaluate(ctxFieldName, ctx); } catch (XPathExpressionException e) { e.printStackTrace(); } if (value != null && value.length() == 0) { throw new RuntimeException("Mandatory field " + ctxFieldName + " not provided, uncompliant UPNP device !!"); } return value; } /** * Parsing an URL from the descriptionXML file * * @param url * the string representation fo the URL * @param baseURL * the base device URL, needed if the url param is relative * @return an URL object defining the url param * @throws MalformedURLException * if the url param or baseURL.toExternalForm() + url cannot be * parsed to create an URL object */ public final static URL getURL(String url, URL baseURL) throws MalformedURLException { URL rtrVal; if (url == null || url.trim().length() == 0) { return null; } try { rtrVal = new URL(url); } catch (MalformedURLException malEx) { // maybe that the url is relative, we add the baseURL and // reparse it // if relative then we take the device baser url root and add // the url if (baseURL != null) { url = url.replace('\\', '/'); if (url.charAt(0) != '/') { // the path is relative to the device baseURL String externalForm = baseURL.toExternalForm(); if (!externalForm.endsWith("/")) { externalForm += "/"; } rtrVal = new URL(externalForm + url); } else { // the path is not relative String URLRoot = baseURL.getProtocol() + "://" + baseURL.getHost() + ":" + baseURL.getPort(); rtrVal = new URL(URLRoot + url); } } else { throw malEx; } } return rtrVal; } /** * Generates a list of all the child ( not only top level, full childrens * hierarchy included ) UPNPDevice objects for this device. * * @return the generated list or null if no child devices bound */ public List<Device> getChildDevices() { if (childDevices == null) { return null; } List<Device> rtrVal = new ArrayList<Device>(); for (Device device : childDevices) { rtrVal.add(device); List<Device> found = device.getChildDevices(); if (found != null) { rtrVal.addAll(found); } } return rtrVal; } /** * Return the parent UPNPDevice, null if the device is an UPNPRootDevice * * @return the parent device instance */ public Device getDirectParent() { return parent; } /** * Looks for a child UPNP device definition file, the whole devices tree * will be searched, starting from the current device node. * * @param deviceURI * the device URI to search * @return An UPNPDevice if anything matches or null */ public Device getChildDevice(String deviceURI) { if (deviceType.equals(deviceURI)) { return this; } if (childDevices == null) { return null; } for (Device device : childDevices) { Device found = device.getChildDevice(deviceURI); if (found != null) { return found; } } return null; } /** * Looks for a UPNP device service definition object for the given service * URI (Type) * * @param serviceURI * the URI of the service * @return A matching UPNPService object or null */ public Service getService(String serviceURI) { if (services == null) { return null; } for (Service service : services) { if (service.serviceType.equals(serviceURI)) { return service; } } return null; } /** * Looks for a UPNP device service definition object for the given service * ID * * @param serviceID * the ID of the service * @return A matching UPNPService object or null */ public Service getServiceByID(String serviceID) { if (services == null) { return null; } for (Service service : services) { if (service.serviceId.equals(serviceID)) { return service; } } return null; } /** * Looks for the all the UPNP device service definition object for the * current UPNP device object. This method can be used to retrieve multiple * same kind ( same service type ) of services with different services id on * a device * * @param serviceURI * the URI of the service * @return A matching List of UPNPService objects or null */ public List<Service> getServices(String serviceURI) { if (services == null) { return null; } List<Service> rtrVal = new ArrayList<Service>(); for (Service service : services) { if (service.serviceType.equals(serviceURI)) { rtrVal.add(service); } } if (rtrVal.size() == 0) { return null; } return rtrVal; } @Override public String toString() { StringBuilder b = new StringBuilder("UPNP device ").append(deviceType); b.append(" ").append(friendlyName); b.append("\n\t").append(manufacturer); b.append(" ").append(manufacturerURL); b.append(" ").append(presentationURL); b.append("\n\t").append(modelName); b.append(" ").append(modelNumber); b.append(" ").append(modelDescription); b.append(" ").append(modelURL); b.append("\n\t").append(serialNumber); b.append("\n\tServices:\n\t\t"); for (int i = 0; i < services.length; i++) { String s = services[i].toString(); s = s.replaceAll("\n", "\n\t\t"); b.append(s); b.append("\n\t\t"); } b.append("\n\tDevices:\n\t\t"); for (int i = 0; i < childDevices.length; i++) { String s = childDevices[i].toString(); s = s.replaceAll("\n", "\n\t"); b.append(s); b.append("\n\t\t"); } return b.toString(); } }