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 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 86 public String getPartTypeName() { 87 return partTypeName; 88 } 89 90 public HashMap getPartParameters() { 91 return partParameters; 92 } 93 94 public String getUninterpretedParameterString() { 95 return uninterpretedParameterString; 96 } 97 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 209 public String getConnectionAndParametersAsString() { 210 return connection.getUninterpretedString(); 211 } 212 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 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 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 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 326 private static boolean isAlphaNumeric(String s) { 327 return isValidString(s, null); 328 } 329 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 */ 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