View Javadoc
1   /*
2     (C) Copyright IBM Corp. 2006, 2009
3   
4     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
5     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
6     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
7   
8     You can obtain a current copy of the Eclipse Public License from
9     http://www.opensource.org/licenses/eclipse-1.0.php
10  
11    @author : Endre Bak, ebak@de.ibm.com
12   * 
13   * Flag       Date        Prog         Description
14   * -------------------------------------------------------------------------------
15   * 1565892    2006-11-05  ebak         Make SBLIM client JSR48 compliant
16   * 1723607    2007-05-22  ebak         IPv6 support in WBEM-URI strings
17   * 1917309    2008-03-24  raman_arora  "/root:__NAMESPACE" not valid CIMObjectPath
18   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
19   * 2204488 	  2008-10-28  raman_arora  Fix code to remove compiler warnings
20   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
21   */
22  
23  package org.metricshub.wbem.sblim.cimclient.internal.uri;
24  
25  /*-
26   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
27   * WBEM Java Client
28   * ჻჻჻჻჻჻
29   * Copyright 2023 - 2025 MetricsHub
30   * ჻჻჻჻჻჻
31   * Licensed under the Apache License, Version 2.0 (the "License");
32   * you may not use this file except in compliance with the License.
33   * You may obtain a copy of the License at
34   *
35   *      http://www.apache.org/licenses/LICENSE-2.0
36   *
37   * Unless required by applicable law or agreed to in writing, software
38   * distributed under the License is distributed on an "AS IS" BASIS,
39   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
40   * See the License for the specific language governing permissions and
41   * limitations under the License.
42   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
43   */
44  
45  import java.util.regex.Pattern;
46  import org.metricshub.wbem.sblim.cimclient.internal.util.MOF;
47  
48  /**
49   * <pre>
50   *    untypedNamespacePath	=	namespacePath
51   *
52   *    untypedClassPath		=	namespacePath &quot;:&quot; className
53   *
54   *    untypedInstancePath	=	namespacePath &quot;:&quot; className &quot;.&quot; key_value_pairs
55   *
56   *    typedNamespacePath	=	namespacePath &quot;/(namespace)&quot;
57   *
58   *    typedClassPath		=	namespacePath &quot;/(class)&quot; className
59   *
60   *    typedInstancePath	= 	namespacePath &quot;/(instance)&quot; className &quot;.&quot; typed_key_value_pairs
61   * </pre>
62   */
63  public class URI {
64  
65  	/**
66  	 * parse
67  	 *
68  	 * @param pUri
69  	 * @return URI
70  	 * @throws IllegalArgumentException
71  	 *             if parsing failed.
72  	 */
73  	public static URI parse(String pUri) throws IllegalArgumentException {
74  		URIString uriStr = new URIString(pUri);
75  		NamespacePath nsPath = NamespacePath.parse(uriStr);
76  		if (nsPath == null) {
77  			String msg = "namespacePath expected!\n" + uriStr.markPosition();
78  			throw new IllegalArgumentException(msg);
79  		}
80  		String uriType = parseUriType(uriStr);
81  		boolean typed = uriType != null;
82  		if (uriStr.length() == 0) {
83  			if (typed && uriType != NAMESPACE) {
84  				String msg = uriType + " excepted but " + NAMESPACE + " found!\n" + uriStr.markPosition();
85  				throw new IllegalArgumentException(msg);
86  			}
87  			return new URI(nsPath, uriType, null, null);
88  		}
89  		if (!typed && !uriStr.cutStarting(':')) {
90  			String msg = "':' expected!\n" + uriStr.markPosition();
91  			throw new IllegalArgumentException(msg);
92  		}
93  		String className = parseClassName(uriStr, typed);
94  		if (uriStr.length() == 0) {
95  			if (typed && uriType != CLASS) {
96  				String msg = uriType + " expected but " + CLASS + " found!\n" + uriStr.markPosition();
97  				throw new IllegalArgumentException(msg);
98  			}
99  			return new URI(nsPath, uriType, className, null);
100 		}
101 		KeyValuePairs keyValuePairs = parseKeyValuePairs(uriStr, typed);
102 		return new URI(nsPath, uriType, className, keyValuePairs);
103 	}
104 
105 	/**
106 	 * <pre>
107 	 *  referenceValue			=	[ namespaceName &quot;:&quot; ] className &quot;.&quot;
108 	 * 								untyped_key_value_pairs
109 	 *
110 	 *  typed_reference_value		=	&quot;(reference)&quot; &quot;\&quot;&quot; typedReferenceValue &quot;\&quot;&quot;
111 	 *
112 	 *  // according to Alexander we have to support instance references only
113 	 *  typedReferenceValue	=	[ namespaceName ] &quot;/(instance)&quot; className &quot;.&quot;
114 	 *  							typed_key_value_pairs
115 	 * </pre>
116 	 *
117 	 * @param pUriStr
118 	 * @param pTyped
119 	 * @return URI
120 	 * @throws IllegalArgumentException
121 	 *             if parsing failed
122 	 */
123 	public static URI parseRef(URIString pUriStr, boolean pTyped) throws IllegalArgumentException {
124 		URIString uriStr = pUriStr.deepCopy();
125 		// try to retrieve optional namespace handle
126 		String namespaceName = NamespaceHandle.parseNamespaceName(uriStr);
127 		if (pTyped) {
128 			if (!uriStr.cutStarting("/(instance)")) {
129 				namespaceName = null;
130 				uriStr.set(pUriStr);
131 			}
132 		} else {
133 			if (!uriStr.cutStarting(':')) {
134 				namespaceName = null;
135 				uriStr.set(pUriStr);
136 			}
137 		}
138 		// className is not typed in reference
139 		String className = parseClassName(uriStr, false);
140 		if (uriStr.length() == 0) {
141 			if (pTyped) return new URI(null, className, null, pTyped);
142 			// untyped reference can be instance reference only
143 			String msg = "Untyped reference can be instance reference only!\n" + uriStr.markPosition();
144 			throw new IllegalArgumentException(msg);
145 		}
146 		KeyValuePairs keyValuePairs = parseKeyValuePairs(uriStr, pTyped);
147 		if (keyValuePairs == null) {
148 			String msg = "Property reference must contain key-value pairs!\n" + uriStr.markPosition();
149 			throw new IllegalArgumentException(msg);
150 		}
151 		pUriStr.set(uriStr);
152 		return new URI(namespaceName, className, keyValuePairs, pTyped);
153 	}
154 
155 	/**
156 	 * getNamespaceType
157 	 *
158 	 * @return String or null if not set
159 	 */
160 	public String getNamespaceType() {
161 		return this.iNamespacePath == null ? null : this.iNamespacePath.getNamespaceType();
162 	}
163 
164 	/**
165 	 * getNamespaceName
166 	 *
167 	 * @return String or null if not set
168 	 */
169 	public String getNamespaceName() {
170 		return this.iNamespacePath == null ? null : this.iNamespacePath.getNamespaceName();
171 	}
172 
173 	/**
174 	 * getUserInfo
175 	 *
176 	 * @return String or null if not set
177 	 */
178 	public String getUserInfo() {
179 		return this.iNamespacePath == null ? null : this.iNamespacePath.getUserInfo();
180 	}
181 
182 	/**
183 	 * getHost Userinfo is attached if exists.
184 	 *
185 	 * @return String or null if not set
186 	 */
187 	public String getHost() {
188 		if (this.iNamespacePath == null) return null;
189 		String userInfo = this.iNamespacePath.getUserInfo();
190 		String host = this.iNamespacePath.getHost();
191 		return userInfo == null ? host : userInfo + '@' + host;
192 	}
193 
194 	/**
195 	 * getPort
196 	 *
197 	 * @return String or null if not set
198 	 */
199 	public String getPort() {
200 		return this.iNamespacePath == null ? null : this.iNamespacePath.getPort();
201 	}
202 
203 	/**
204 	 * getClassName
205 	 *
206 	 * @return String or null if not set
207 	 */
208 	public String getClassName() {
209 		return this.iClassName;
210 	}
211 
212 	/**
213 	 * getKeyValuePairs
214 	 *
215 	 * @return KeyValuePairs or null if not set
216 	 */
217 	public KeyValuePairs getKeyValuePairs() {
218 		return this.iKeyValuePairs;
219 	}
220 
221 	/**
222 	 * @see java.lang.Object#toString()
223 	 */
224 	@Override
225 	public String toString() {
226 		StringBuffer buf = new StringBuffer();
227 		if (this.iNamespacePath != null) {
228 			if (this.iIsRef) buf.append(getNamespaceName()); else buf.append(this.iNamespacePath.toString());
229 		}
230 		if (this.iUriType != null) buf.append("/(" + this.iUriType + ")");
231 		if (this.iClassName != null) {
232 			if (this.iNamespacePath != null && this.iUriType == null) {
233 				// it's not a reference value
234 				buf.append(':');
235 			}
236 			buf.append(this.iClassName);
237 			if (this.iKeyValuePairs != null) {
238 				buf.append('.');
239 				buf.append(this.iKeyValuePairs.toString());
240 			}
241 		}
242 		return buf.toString();
243 	}
244 
245 	/**
246 	 * Constructor for URI paths.
247 	 *
248 	 * @param pNamespacePath
249 	 * @param pUriType
250 	 * @param pClassName
251 	 * @param pKeyValuePairs
252 	 */
253 	private URI(NamespacePath pNamespacePath, String pUriType, String pClassName, KeyValuePairs pKeyValuePairs) {
254 		this.iNamespacePath = pNamespacePath;
255 		this.iUriType = pUriType;
256 		this.iClassName = pClassName;
257 		this.iKeyValuePairs = pKeyValuePairs;
258 		this.iIsRef = false;
259 	}
260 
261 	/**
262 	 * Constructor for reference property values.
263 	 *
264 	 * @param pNamespaceName
265 	 * @param pClassName
266 	 * @param pKeyValuePairs
267 	 * @param pTyped
268 	 */
269 	private URI(String pNamespaceName, String pClassName, KeyValuePairs pKeyValuePairs, boolean pTyped) {
270 		if (pNamespaceName == null && pClassName == null) {
271 			String msg = "pNamespaceName or pClassName must be set!";
272 			throw new IllegalArgumentException(msg);
273 		}
274 		if (pKeyValuePairs != null && pClassName == null) {
275 			String msg = "If pKeyValuePairs is set pClassName cannot be null!";
276 			throw new IllegalArgumentException(msg);
277 		}
278 		this.iNamespacePath = pNamespaceName == null ? null : new NamespacePath(pNamespaceName);
279 		if (pTyped) {
280 			if (pKeyValuePairs != null) this.iUriType = INSTANCE; else if (pClassName != null) this.iUriType =
281 				CLASS; else this.iUriType = NAMESPACE;
282 		} else this.iUriType = null;
283 		this.iClassName = pClassName;
284 		this.iKeyValuePairs = pKeyValuePairs;
285 		this.iIsRef = true;
286 	}
287 
288 	private NamespacePath iNamespacePath;
289 
290 	/**
291 	 * iUriType - if null the URI is untyped
292 	 */
293 	private String iUriType;
294 
295 	private String iClassName;
296 
297 	private KeyValuePairs iKeyValuePairs;
298 
299 	private boolean iIsRef;
300 
301 	private static final String NAMESPACE = MOF.NAMESPACE, CLASS = MOF.CLASS, INSTANCE = MOF.INSTANCE;
302 
303 	/**
304 	 * uriType = "/(" ( "namespace" / "class" / "instance" ) ")"
305 	 *
306 	 * @param pUriStr
307 	 * @return NAMESPACE | CLASS | INSTANCE
308 	 */
309 	private static String parseUriType(URIString pUriStr) {
310 		URIString uriStr = pUriStr.deepCopy();
311 		if (!uriStr.cutStarting("/(")) return null;
312 		String typeStr = uriStr.removeTill(')', true, true);
313 		if (typeStr == null) return null;
314 		if (typeStr.equalsIgnoreCase(NAMESPACE)) typeStr = NAMESPACE; else if (typeStr.equalsIgnoreCase(CLASS)) typeStr =
315 			CLASS; else if (typeStr.equalsIgnoreCase(INSTANCE)) typeStr = INSTANCE; else return null;
316 		pUriStr.set(uriStr);
317 		return typeStr;
318 	}
319 
320 	/*
321 	 * Per DMTF DSP0004 spec: Therefore, all namespace, class and property names
322 	 * are identifiers composed as follows: 1. Initial identifier characters
323 	 * must be in set S1, where S1 = {U+005F, U+0041...U+005A, U+0061...U+007A,
324 	 * U+0080...U+FFEF) [This is alphabetic, plus underscore] 2. All following
325 	 * characters must be in set S2 where S2 = S1 union {U+0030...U+0039} [This
326 	 * is alphabetic, underscore, plus Arabic numerals 0 through 9.]
327 	 */
328 	private static final Pattern PAT = Pattern.compile("^([A-Za-z_]+[A-Za-z0-9_]*).*");
329 
330 	/**
331 	 * @param pTyped
332 	 */
333 	private static String parseClassName(URIString pUriStr, boolean pTyped) throws IllegalArgumentException {
334 		if (!pUriStr.matchAndCut(PAT, 1)) {
335 			String msg = "className expected!\n" + pUriStr.markPosition();
336 			throw new IllegalArgumentException(msg);
337 		}
338 		String className = pUriStr.group(1);
339 		return className;
340 	}
341 
342 	private static KeyValuePairs parseKeyValuePairs(URIString pUriStr, boolean pTyped) throws IllegalArgumentException {
343 		if (!pUriStr.cutStarting('.')) {
344 			String msg = "'.' expected!\n" + pUriStr.markPosition();
345 			throw new IllegalArgumentException(msg);
346 		}
347 		KeyValuePairs keyValuePairs = KeyValuePairs.parse(pTyped, pUriStr);
348 		if (keyValuePairs == null) {
349 			String msg = "keyValuePairs expected!\n" + pUriStr.markPosition();
350 			throw new IllegalArgumentException(msg);
351 		}
352 		return keyValuePairs;
353 	}
354 }