1
2
3
4
5
6
7
8
9
10
11
12
13
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
26
27
28
29
30
31
32
33 public class ServiceType {
34
35 final private static Logger log = Logger.getLogger(ServiceType.class.getName());
36
37 public static final Pattern PATTERN =
38 Pattern.compile("urn:(" + Constants.REGEX_NAMESPACE + "):service:(" + Constants.REGEX_TYPE + "):([0-9]+).*");
39
40
41 public static final Pattern BROKEN_PATTERN =
42 Pattern.compile("urn:(" + Constants.REGEX_NAMESPACE + "):serviceId:(" + Constants.REGEX_TYPE + "):([0-9]+).*");
43
44 private String namespace;
45 private String type;
46 private int version = 1;
47
48 public ServiceType(String namespace, String type) {
49 this(namespace, type, 1);
50 }
51
52 public ServiceType(String namespace, String type, int version) {
53
54 if (namespace != null && !namespace.matches(Constants.REGEX_NAMESPACE)) {
55 throw new IllegalArgumentException("Service type namespace contains illegal characters");
56 }
57 this.namespace = namespace;
58
59 if (type != null && !type.matches(Constants.REGEX_TYPE)) {
60 throw new IllegalArgumentException("Service type suffix too long (64) or contains illegal characters");
61 }
62 this.type = type;
63
64 this.version = version;
65 }
66
67 public String getNamespace() {
68 return namespace;
69 }
70
71 public String getType() {
72 return type;
73 }
74
75 public int getVersion() {
76 return version;
77 }
78
79
80
81
82 public static ServiceType valueOf(String s) throws InvalidValueException {
83
84 if (s == null)
85 throw new InvalidValueException("Can't parse null string");
86
87 ServiceType serviceType = null;
88
89
90 s = s.replaceAll("\\s", "");
91
92
93 try {
94 serviceType = UDAServiceType.valueOf(s);
95 } catch (Exception ex) {
96
97 }
98
99 if (serviceType != null)
100 return serviceType;
101
102
103 try {
104 Matcher matcher = ServiceType.PATTERN.matcher(s);
105 if (matcher.matches() && matcher.groupCount() >= 3) {
106 return new ServiceType(matcher.group(1), matcher.group(2), Integer.valueOf(matcher.group(3)));
107 }
108
109 matcher = ServiceType.BROKEN_PATTERN.matcher(s);
110 if (matcher.matches() && matcher.groupCount() >= 3) {
111 return new ServiceType(matcher.group(1), matcher.group(2), Integer.valueOf(matcher.group(3)));
112 }
113
114
115
116 matcher = Pattern.compile("urn:(" + Constants.REGEX_NAMESPACE + "):service:(.+?):([0-9]+).*").matcher(s);
117 if (matcher.matches() && matcher.groupCount() >= 3) {
118 String cleanToken = matcher.group(2).replaceAll("[^a-zA-Z_0-9\\-]", "-");
119 log.warning(
120 "UPnP specification violation, replacing invalid service type token '"
121 + matcher.group(2)
122 + "' with: "
123 + cleanToken
124 );
125 return new ServiceType(matcher.group(1), cleanToken, Integer.valueOf(matcher.group(3)));
126 }
127
128
129
130 matcher = Pattern.compile("urn:(" + Constants.REGEX_NAMESPACE + "):serviceId:(.+?):([0-9]+).*").matcher(s);
131 if (matcher.matches() && matcher.groupCount() >= 3) {
132 String cleanToken = matcher.group(2).replaceAll("[^a-zA-Z_0-9\\-]", "-");
133 log.warning(
134 "UPnP specification violation, replacing invalid service type token '"
135 + matcher.group(2)
136 + "' with: "
137 + cleanToken
138 );
139 return new ServiceType(matcher.group(1), cleanToken, Integer.valueOf(matcher.group(3)));
140 }
141 } catch (RuntimeException e) {
142 throw new InvalidValueException(String.format(
143 "Can't parse service type string (namespace/type/version) '%s': %s", s, e.toString()
144 ));
145 }
146
147 throw new InvalidValueException("Can't parse service type string (namespace/type/version): " + s);
148 }
149
150
151
152
153
154 public boolean implementsVersion(ServiceType that) {
155 if (that == null) return false;
156 if (!namespace.equals(that.namespace)) return false;
157 if (!type.equals(that.type)) return false;
158 if (version < that.version) return false;
159 return true;
160 }
161
162 public String toFriendlyString() {
163 return getNamespace() + ":" + getType() + ":" + getVersion();
164 }
165
166 @Override
167 public String toString() {
168 return "urn:" + getNamespace() + ":service:" + getType() + ":" + getVersion();
169 }
170
171 @Override
172 public boolean equals(Object o) {
173 if (this == o) return true;
174 if (o == null || !(o instanceof ServiceType)) return false;
175
176 ServiceType that = (ServiceType) o;
177
178 if (version != that.version) return false;
179 if (!namespace.equals(that.namespace)) return false;
180 if (!type.equals(that.type)) return false;
181
182 return true;
183 }
184
185 @Override
186 public int hashCode() {
187 int result = namespace.hashCode();
188 result = 31 * result + type.hashCode();
189 result = 31 * result + version;
190 return result;
191 }
192 }