xref: /AOO41X/main/javaunohelper/com/sun/star/lib/uno/helper/UnoUrl.java (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 package com.sun.star.lib.uno.helper;
29 import java.io.UnsupportedEncodingException;
30 import java.util.HashMap;
31 import java.util.Vector;
32 
33 /**
34  * Object representation and parsing of Uno Urls,
35  * which allow to locate a named Uno object in a
36  * different process. An Uno Url consists of the
37  * specification of a connection, protocol and
38  * rootOid delimited with a ';'.
39  * The syntax of an Uno Url is
40  *
41  * <code>
42  * [uno:]connection-type,parameters;protocol-name,parameters;objectname";
43  * </code>
44  *
45  * An example Uno Url will look like this:
46  *
47  * <code>
48  * socket,host=localhost,port=2002;urp;StarOffice.ServiceManager
49  * </code>
50  *
51  * For more information about Uno Url please consult
52  * <a href="http://udk.openoffice.org/common/man/spec/uno-url.html">
53  * http://udk.openoffice.org/common/man/spec/uno-url.html</a>
54  *
55  * Usage:
56  *
57  * <code>
58  *   UnoUrl url = UnoUrl.parseUnoUrl("socket,host=localhost,port=2002;urp;StarOffice.ServiceManager");
59  * </code>
60  *
61  * @author Joerg Brunsmann
62  */
63 public class UnoUrl {
64 
65 	private static final String FORMAT_ERROR =
66 		"syntax: [uno:]connection-type,parameters;protocol-name,parameters;objectname";
67 
68 	private static final String VALUE_CHAR_SET = "!$&'()*+-./:?@_~";
69 	private static final String OID_CHAR_SET = VALUE_CHAR_SET + ",=";
70 
71 	private UnoUrlPart connection;
72 	private UnoUrlPart protocol;
73 	private String rootOid;
74 
75 	static private class UnoUrlPart {
76 
77 		private String partTypeName;
78 		private HashMap partParameters;
79 		private String uninterpretedParameterString;
80 
81 		public UnoUrlPart(
82 			String uninterpretedParameterString,
83 			String partTypeName,
84 			HashMap partParameters) {
85 			this.uninterpretedParameterString = uninterpretedParameterString;
86 			this.partTypeName = partTypeName;
87 			this.partParameters = partParameters;
88 		}
89 
90 		public String getPartTypeName() {
91 			return partTypeName;
92 		}
93 
94 		public HashMap getPartParameters() {
95 			return partParameters;
96 		}
97 
98 		public String getUninterpretedParameterString() {
99 			return uninterpretedParameterString;
100 		}
101 
102         public String getUninterpretedString() {
103             StringBuffer buf = new StringBuffer(partTypeName);
104             if (uninterpretedParameterString.length() > 0) {
105                 buf.append(',');
106                 buf.append(uninterpretedParameterString);
107             }
108             return buf.toString();
109         }
110 	}
111 
112 	private UnoUrl(
113 		UnoUrlPart connectionPart,
114 		UnoUrlPart protocolPart,
115 		String rootOid) {
116 		this.connection = connectionPart;
117 		this.protocol = protocolPart;
118 		this.rootOid = rootOid;
119 	}
120 
121 	/**
122 	 * Returns the name of the connection of this
123 	 * Uno Url. Encoded characters are not allowed.
124 	 *
125 	 * @return The connection name as string.
126 	 */
127 	public String getConnection() {
128 		return connection.getPartTypeName();
129 	}
130 
131 	/**
132 	 * Returns the name of the protocol of this
133 	 * Uno Url. Encoded characters are not allowed.
134 	 *
135 	 * @return The protocol name as string.
136 	 */
137 	public String getProtocol() {
138 		return protocol.getPartTypeName();
139 	}
140 
141 	/**
142 	 * Return the object name. Encoded character are
143 	 * not allowed.
144 	 *
145 	 * @return The object name as String.
146 	 */
147 	public String getRootOid() {
148 		return rootOid;
149 	}
150 
151 	/**
152 	 * Returns the protocol parameters as
153 	 * a Hashmap with key/value pairs. Encoded
154 	 * characters like '%41' are decoded.
155 	 *
156 	 * @return a HashMap with key/value pairs for protocol parameters.
157 	 */
158 	public HashMap getProtocolParameters() {
159 		return protocol.getPartParameters();
160 	}
161 
162 	/**
163 	 * Returns the connection parameters as
164 	 * a Hashmap with key/value pairs. Encoded
165 	 * characters like '%41' are decoded.
166 	 *
167 	 * @return a HashMap with key/value pairs for connection parameters.
168 	 */
169 	public HashMap getConnectionParameters() {
170 		return connection.getPartParameters();
171 	}
172 
173 	/**
174 	 * Returns the raw specification of the protocol
175 	 * parameters. Encoded characters like '%41' are
176 	 * not decoded.
177 	 *
178 	 * @return The uninterpreted protocol parameters as string.
179 	 */
180 	public String getProtocolParametersAsString() {
181 		return protocol.getUninterpretedParameterString();
182 	}
183 
184 	/**
185 	 * Returns the raw specification of the connection
186 	 * parameters. Encoded characters like '%41' are
187 	 * not decoded.
188 	 *
189 	 * @return The uninterpreted connection parameters as string.
190 	 */
191 	public String getConnectionParametersAsString() {
192 		return connection.getUninterpretedParameterString();
193 	}
194 
195     /**
196      * Returns the raw specification of the protocol
197      * name and parameters. Encoded characters like '%41' are
198      * not decoded.
199      *
200      * @return The uninterpreted protocol name and parameters as string.
201      */
202     public String getProtocolAndParametersAsString() {
203         return protocol.getUninterpretedString();
204     }
205 
206     /**
207      * Returns the raw specification of the connection
208      * name and parameters. Encoded characters like '%41' are
209      * not decoded.
210      *
211      * @return The uninterpreted connection name and parameters as string.
212      */
213     public String getConnectionAndParametersAsString() {
214         return connection.getUninterpretedString();
215     }
216 
217 	private static int hexToInt(int ch)
218 		throws com.sun.star.lang.IllegalArgumentException {
219 		int c = Character.toLowerCase((char) ch);
220 		boolean isDigit = ('0' <= c && c <= '9');
221 		boolean isValidChar = ('a' <= c && c <= 'f') || isDigit;
222 
223 		if (!isValidChar)
224 			throw new com.sun.star.lang.IllegalArgumentException(
225 				"Invalid UTF-8 hex byte '" + c + "'.");
226 
227 		return isDigit ? ch - '0' : 10 + ((char) c - 'a') & 0xF;
228 	}
229 
230 	private static String decodeUTF8(String s)
231 		throws com.sun.star.lang.IllegalArgumentException {
232 		Vector v = new Vector();
233 
234 		for (int i = 0; i < s.length(); i++) {
235 			int ch = s.charAt(i);
236 
237 			if (ch == '%') {
238 				int hb = hexToInt(s.charAt(++i));
239 				int lb = hexToInt(s.charAt(++i));
240 				ch = (hb << 4) | lb;
241 			}
242 
243 			v.addElement(new Integer(ch));
244 		}
245 
246 		int size = v.size();
247 		byte[] bytes = new byte[size];
248 		for (int i = 0; i < size; i++) {
249 			Integer anInt = (Integer) v.elementAt(i);
250 			bytes[i] = (byte) (anInt.intValue() & 0xFF);
251 		}
252 
253 		try {
254 			return new String(bytes, "UTF-8");
255 		} catch (UnsupportedEncodingException e) {
256 			throw new com.sun.star.lang.IllegalArgumentException(
257 				"Couldn't convert parameter string to UTF-8 string:" + e.getMessage());
258 		}
259 	}
260 
261 	private static HashMap buildParamHashMap(String paramString)
262 		throws com.sun.star.lang.IllegalArgumentException {
263 		HashMap params = new HashMap();
264 
265 		int pos = 0;
266 
267 		while (true) {
268 			char c = ',';
269 			String aKey = "";
270 			String aValue = "";
271 
272 			while ((pos < paramString.length())
273 				&& ((c = paramString.charAt(pos++)) != '=')) {
274 				aKey += c;
275 			}
276 
277 			while ((pos < paramString.length())
278 				&& ((c = paramString.charAt(pos++)) != ',')
279 				&& c != ';') {
280 				aValue += c;
281 			}
282 
283 			if ((aKey.length() > 0) && (aValue.length() > 0)) {
284 
285 				if (!isAlphaNumeric(aKey)) {
286 					throw new com.sun.star.lang.IllegalArgumentException(
287 						"The parameter key '"
288 							+ aKey
289 							+ "' may only consist of alpha numeric ASCII characters.");
290 				}
291 
292 				if (!isValidString(aValue, VALUE_CHAR_SET + "%")) {
293 					throw new com.sun.star.lang.IllegalArgumentException(
294 						"The parameter value for key '" + aKey + "' contains illegal characters.");
295 				}
296 
297 				params.put(aKey, decodeUTF8(aValue));
298 			}
299 
300 			if ((pos >= paramString.length()) || (c != ','))
301 				break;
302 
303 		}
304 
305 		return params;
306 	}
307 
308 	private static UnoUrlPart parseUnoUrlPart(String thePart)
309 		throws com.sun.star.lang.IllegalArgumentException {
310 		String partName = thePart;
311 		String theParamPart = "";
312 		int index = thePart.indexOf(",");
313 		if (index != -1) {
314 			partName = thePart.substring(0, index).trim();
315 			theParamPart = thePart.substring(index + 1).trim();
316 		}
317 
318 		if (!isAlphaNumeric(partName)) {
319 			throw new com.sun.star.lang.IllegalArgumentException(
320 				"The part name '"
321 					+ partName
322 					+ "' may only consist of alpha numeric ASCII characters.");
323 		}
324 
325 		HashMap params = buildParamHashMap(theParamPart);
326 
327 		return new UnoUrlPart(theParamPart, partName, params);
328 	}
329 
330 	private static boolean isAlphaNumeric(String s) {
331 		return isValidString(s, null);
332 	}
333 
334 	private static boolean isValidString(String identifier, String validCharSet) {
335 
336 		int len = identifier.length();
337 
338 		for (int i = 0; i < len; i++) {
339 
340 			int ch = identifier.charAt(i);
341 
342 			boolean isValidChar =
343 				('A' <= ch && ch <= 'Z')
344 					|| ('a' <= ch && ch <= 'z')
345 					|| ('0' <= ch && ch <= '9');
346 
347 			if (!isValidChar && (validCharSet != null)) {
348 				isValidChar = (validCharSet.indexOf(ch) != -1);
349 			}
350 
351 			if (!isValidChar)
352 				return false;
353 		}
354 
355 		return true;
356 	}
357 
358 	/**
359 	 * Parses the given Uno Url and returns
360 	 * an in memory object representation.
361 	 *
362 	 * @param unoUrl The given uno URl as string.
363 	 * @return Object representation of class UnoUrl.
364 	 * @throws IllegalArgumentException if Url cannot be parsed.
365 	 */
366 	public static UnoUrl parseUnoUrl(String unoUrl)
367 		throws com.sun.star.lang.IllegalArgumentException {
368 
369 		String url = unoUrl;
370 
371 		int index = url.indexOf(':');
372 		if (index != -1) {
373 			String unoStr = url.substring(0, index).trim();
374 			if (!"uno".equals(unoStr)) {
375 				throw new com.sun.star.lang.IllegalArgumentException(
376 					"Uno Urls must start with 'uno:'. " + FORMAT_ERROR);
377 			}
378 		}
379 
380 		url = url.substring(index + 1).trim();
381 
382 		index = url.indexOf(';');
383 		if (index == -1) {
384 			throw new com.sun.star.lang.IllegalArgumentException("'"+unoUrl+"' is an invalid Uno Url. " + FORMAT_ERROR);
385 		}
386 
387 		String connection = url.substring(0, index).trim();
388 		url = url.substring(index + 1).trim();
389 
390 		UnoUrlPart connectionPart = parseUnoUrlPart(connection);
391 
392 		index = url.indexOf(';');
393 		if (index == -1) {
394 			throw new com.sun.star.lang.IllegalArgumentException("'"+unoUrl+"' is an invalid Uno Url. " + FORMAT_ERROR);
395 		}
396 
397 		String protocol = url.substring(0, index).trim();
398 		url = url.substring(index + 1).trim();
399 
400 		UnoUrlPart protocolPart = parseUnoUrlPart(protocol);
401 
402 		String rootOid = url.trim();
403 		if (!isValidString(rootOid, OID_CHAR_SET)) {
404 			throw new com.sun.star.lang.IllegalArgumentException(
405 				"Root OID '"+ rootOid + "' contains illegal characters.");
406 		}
407 
408 		return new UnoUrl(connectionPart, protocolPart, rootOid);
409 
410 	}
411 
412 }
413