View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.model.types;
17  
18  import org.fourthline.cling.model.Constants;
19  
20  import java.util.logging.Logger;
21  import java.util.regex.Pattern;
22  import java.util.regex.Matcher;
23  
24  /**
25   * Represents a service identifier, for example <code>urn:my-domain-namespace:serviceId:MyService123</code>
26   *
27   * @author Christian Bauer
28   */
29  public class ServiceId {
30  
31      final private static Logger log = Logger.getLogger(ServiceId.class.getName());
32  
33      public static final String UNKNOWN = "UNKNOWN";
34  
35      public static final Pattern PATTERN =
36          Pattern.compile("urn:(" + Constants.REGEX_NAMESPACE + "):serviceId:(" + Constants.REGEX_ID + ")");
37  
38      // Note: 'service' vs. 'serviceId'
39      public static final Pattern BROKEN_PATTERN =
40                 Pattern.compile("urn:(" + Constants.REGEX_NAMESPACE + "):service:(" + Constants.REGEX_ID+ ")");
41  
42      private String namespace;
43      private String id;
44  
45      public ServiceId(String namespace, String id) {
46          if (namespace != null && !namespace.matches(Constants.REGEX_NAMESPACE)) {
47              throw new IllegalArgumentException("Service ID namespace contains illegal characters");
48          }
49          this.namespace = namespace;
50  
51          if (id != null && !id.matches(Constants.REGEX_ID)) {
52              throw new IllegalArgumentException("Service ID suffix too long (64) or contains illegal characters");
53          }
54          this.id = id;
55      }
56  
57      public String getNamespace() {
58          return namespace;
59      }
60  
61      public String getId() {
62          return id;
63      }
64  
65      public static ServiceId valueOf(String s) throws InvalidValueException {
66  
67          ServiceId serviceId = null;
68  
69          // First try UDAServiceId parse
70          try {
71              serviceId = UDAServiceId.valueOf(s);
72          } catch (Exception ex) {
73              // Ignore
74          }
75  
76          if (serviceId != null)
77              return serviceId;
78  
79          // Now try a generic ServiceId parse
80          Matcher matcher = ServiceId.PATTERN.matcher(s);
81          if (matcher.matches() && matcher.groupCount() >= 2) {
82              return new ServiceId(matcher.group(1), matcher.group(2));
83          }
84  
85          matcher = ServiceId.BROKEN_PATTERN.matcher(s);
86          if (matcher.matches() && matcher.groupCount() >= 2) {
87              return new ServiceId(matcher.group(1), matcher.group(2));
88          }
89  
90          // TODO: UPNP VIOLATION: Kodak Media Server doesn't provide any service ID token
91          // urn:upnp-org:serviceId:
92          matcher = Pattern.compile("urn:(" + Constants.REGEX_NAMESPACE + "):serviceId:").matcher(s);
93          if (matcher.matches() && matcher.groupCount() >= 1) {
94              log.warning("UPnP specification violation, no service ID token, defaulting to " + UNKNOWN + ": " + s);
95              return new ServiceId(matcher.group(1), UNKNOWN);
96          }
97  
98          // TODO: UPNP VIOLATION: PS Audio Bridge has invalid service IDs
99          String tokens[] = s.split("[:]");
100         if (tokens.length == 4) {
101             log.warning("UPnP specification violation, trying a simple colon-split of: " + s);
102             return new ServiceId(tokens[1], tokens[3]);
103         }
104 
105         throw new InvalidValueException("Can't parse service ID string (namespace/id): " + s);
106     }
107 
108     @Override
109     public String toString() {
110         return "urn:" + getNamespace() + ":serviceId:" + getId();
111     }
112 
113     @Override
114     public boolean equals(Object o) {
115         if (this == o) return true;
116         if (o == null || !(o instanceof ServiceId)) return false;
117 
118         ServiceId serviceId = (ServiceId) o;
119 
120         if (!id.equals(serviceId.id)) return false;
121         if (!namespace.equals(serviceId.namespace)) return false;
122 
123         return true;
124     }
125 
126     @Override
127     public int hashCode() {
128         int result = namespace.hashCode();
129         result = 31 * result + id.hashCode();
130         return result;
131     }
132 }