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