xref: /trunk/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