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