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