]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/util/URIStringUtils.java
Merge commit '4a2d762'
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / util / URIStringUtils.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 /* The following copyright is attached because marked parts of the following code are\r
13  * copied and modified from Jena 2.4.\r
14  */\r
15 /*\r
16  *  (c) Copyright 2001, 2002, 2003, 2004, 2005, 2006 Hewlett-Packard Development Company, LP\r
17  *  All rights reserved.\r
18  *\r
19  * Redistribution and use in source and binary forms, with or without\r
20  * modification, are permitted provided that the following conditions\r
21  * are met:\r
22  * 1. Redistributions of source code must retain the above copyright\r
23  *    notice, this list of conditions and the following disclaimer.\r
24  * 2. Redistributions in binary form must reproduce the above copyright\r
25  *    notice, this list of conditions and the following disclaimer in the\r
26  *    documentation and/or other materials provided with the distribution.\r
27  * 3. The name of the author may not be used to endorse or promote products\r
28  *    derived from this software without specific prior written permission.\r
29 \r
30  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\r
31  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r
32  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r
33  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\r
34  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
35  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
36  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
37  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
38  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r
39  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
40 \r
41  * * Id: URIref.java,v 1.5 2006/03/22 13:52:49 andy_seaborne Exp\r
42 \r
43    AUTHOR:  Jeremy J. Carroll\r
44  */\r
45 \r
46 package org.simantics.databoard.util;\r
47 \r
48 import java.nio.charset.Charset;\r
49 import java.util.ArrayList;\r
50 import java.util.List;\r
51 \r
52 \r
53 /**\r
54  * Contains utility methods for handling URI Strings in the context of ProCore\r
55  * and the Simantics platform. This includes URI escaping and unescaping and\r
56  * namespace/local name separation and joining.\r
57  * \r
58  * <p>\r
59  * URI's in this context are assumed to be formed as follows:\r
60  * \r
61  * <pre>\r
62  * &lt;namespace part&gt;#&lt;local name part&gt;\r
63  * </pre>\r
64  * \r
65  * <p>\r
66  * The implementation of {@link #escape(String)} and {@link #unescape(String)}\r
67  * is copied and modified from Jena's com.hp.hpl.jena.util.URIref.\r
68  * </p>\r
69  * \r
70  * @see <a href="http://en.wikipedia.org/wiki/Percent-encoding">Percent-encoding</a>\r
71  * \r
72  * @author Tuukka Lehtonen\r
73  */\r
74 public final class URIStringUtils {\r
75 \r
76     /**\r
77      * The character '/' is used as a path separator in URI namespace parts in ProCore.\r
78      */\r
79     public static final char NAMESPACE_PATH_SEPARATOR  = '/';\r
80 \r
81     /**\r
82      * The '#' character is used to separate the local name and namespace parts\r
83      * of an URI, for example <code>http://www.example.org#localName</code>.\r
84      */\r
85     public static final char NAMESPACE_LOCAL_SEPARATOR = '#';\r
86 \r
87     /**\r
88      * Checks that only one separator character ({@link #NAMESPACE_LOCAL_SEPARATOR})\r
89      * between namespace and localname exists in the specified URI and returns\r
90      * its index.\r
91      * \r
92      * @param uri the URI to search from\r
93      * @return the character index of the separator ranging from 0 to uri.length()-1\r
94      * @throws IllegalArgumentException if no {@link #NAMESPACE_LOCAL_SEPARATOR}\r
95      *         is found in the specified URI\r
96      */\r
97     private static int assertSingleSeparatorPosition(String uri) {\r
98         int sharpIndex = uri.indexOf(NAMESPACE_LOCAL_SEPARATOR);\r
99         if (sharpIndex == -1) {\r
100             throw new IllegalArgumentException("URI '" + uri + "' does not contain any '" + NAMESPACE_LOCAL_SEPARATOR + "' separator characters");\r
101         }\r
102         int nextSharpIndex = uri.indexOf(NAMESPACE_LOCAL_SEPARATOR, sharpIndex + 1);\r
103         if (nextSharpIndex != -1) {\r
104             throw new IllegalArgumentException("URI '" + uri + "' contains multiple '" + NAMESPACE_LOCAL_SEPARATOR + "' separator characters");\r
105         }\r
106         return sharpIndex;\r
107     }\r
108 \r
109     /**\r
110      * Checks that only one separator character (\r
111      * {@link #NAMESPACE_LOCAL_SEPARATOR}) between namespace and localname\r
112      * exists in the specified URI and returns its index. This version does not\r
113      * throw an exception when the separator is not found.\r
114      * \r
115      * @param uri the URI to search from\r
116      * @return the character index of the separator ranging from 0 to\r
117      *         uri.length()-1 or -1 if no separator was found.\r
118      */\r
119     private static int singleSeparatorPosition(String uri) {\r
120         int sharpIndex = uri.indexOf(NAMESPACE_LOCAL_SEPARATOR);\r
121         if (sharpIndex == -1) {\r
122             return -1;\r
123         }\r
124         int nextSharpIndex = uri.indexOf(NAMESPACE_LOCAL_SEPARATOR, sharpIndex + 1);\r
125         if (nextSharpIndex != -1) {\r
126             return -1;\r
127         }\r
128         return sharpIndex;\r
129     }\r
130 \r
131     /**\r
132      * Splits the specified URI into a namespace and a local name and returns\r
133      * the namespace.\r
134      * \r
135      * <p>\r
136      * Assumes that namespaces are always separated by\r
137      * {@link #NAMESPACE_LOCAL_SEPARATOR} characters.\r
138      * </p>\r
139      * \r
140      * @param uri the URI to split, must be non-null\r
141      * @return the namespace part of the specified URI\r
142      * @throws IllegalArgumentException for URIs without a\r
143      *         {@link #NAMESPACE_LOCAL_SEPARATOR}\r
144      * @throws NullPointerException for <code>null</code> URIs\r
145      */\r
146     public static String getNamespace(String uri) {\r
147         if (uri == null)\r
148             throw new NullPointerException("null uri");\r
149         int separatorIndex = assertSingleSeparatorPosition(uri);\r
150         return uri.substring(0, separatorIndex);\r
151     }\r
152     \r
153     public static String getRVIParent(String uri) {\r
154         int childSeparator = uri.lastIndexOf(URIStringUtils.NAMESPACE_PATH_SEPARATOR);\r
155         int propertySeparator = uri.lastIndexOf(URIStringUtils.NAMESPACE_LOCAL_SEPARATOR);\r
156         int separator = Math.max(childSeparator, propertySeparator);\r
157         return uri.substring(0, separator);\r
158     }\r
159     \r
160 \r
161     /**\r
162      * Splits the specified URI into a namespace and a local name and returns\r
163      * the local name.\r
164      * \r
165      * <p>\r
166      * Assumes that namespaces are always separated by\r
167      * {@link #NAMESPACE_LOCAL_SEPARATOR} characters.\r
168      * </p>\r
169      * \r
170      * @param uri the URI to split, must be non-null\r
171      * @return the local name part of the specified URI\r
172      * @throws IllegalArgumentException for URIs without a\r
173      *         {@link #NAMESPACE_LOCAL_SEPARATOR}\r
174      * @throws NullPointerException for <code>null</code> URIs\r
175      */\r
176     public static String getLocalName(String uri) {\r
177         if (uri == null)\r
178             throw new NullPointerException("null uri");\r
179         int separatorIndex = assertSingleSeparatorPosition(uri);\r
180         return uri.substring(separatorIndex + 1);\r
181     }\r
182 \r
183     public static String escapeName(String name) {\r
184         char[] chars = name.toCharArray();\r
185         boolean modified = false;\r
186         for(int i=0;i<chars.length;++i)\r
187             if(!Character.isJavaIdentifierPart(chars[i])) {\r
188                 chars[i] = '_';\r
189                 modified = true;\r
190             }\r
191         if(modified)\r
192             return new String(chars);\r
193         else\r
194             return name;\r
195     }\r
196 \r
197     final private static int HTTP_POSITION = "http://".length();\r
198 \r
199     public static String[] splitURI(String uri) {\r
200         int nextPathSeparator = uri.lastIndexOf(URIStringUtils.NAMESPACE_PATH_SEPARATOR);\r
201         if (nextPathSeparator == -1) return null;\r
202         if (nextPathSeparator == HTTP_POSITION - 1) {\r
203             if(uri.startsWith("http://")) return new String[] { "http://", uri.substring(HTTP_POSITION, uri.length()) };\r
204             else return null;\r
205         }\r
206         return new String[] {\r
207                 uri.substring(0, nextPathSeparator),\r
208                 uri.substring(nextPathSeparator + 1, uri.length())\r
209         };\r
210     }\r
211     \r
212     public static List<String> splitURISCL(String uri) {\r
213         String[] result = splitURI(uri);\r
214         ArrayList<String> list = new ArrayList<String>(result.length);\r
215         for(String s : result) list.add(s);\r
216         return list;\r
217     }\r
218 \r
219     /**\r
220      * Splits the specified URI into a namespace and a local name and returns\r
221      * them both separately as an array.\r
222      * \r
223      * @param uri the URI to split, must be non-null\r
224      * @return [0] = namespace, [1] = local name or <code>null</code> if the URI\r
225      *         cannot be split.\r
226      * @throws NullPointerException for <code>null</code> URIs\r
227      */\r
228     public static String[] trySplitNamespaceAndLocalName(String uri) {\r
229         if (uri == null)\r
230             throw new NullPointerException("null uri");\r
231         int separatorIndex = singleSeparatorPosition(uri);\r
232         return separatorIndex == -1 ?\r
233                 null\r
234                 : new String[] { uri.substring(0, separatorIndex), uri.substring(separatorIndex + 1) };\r
235     }\r
236 \r
237     /**\r
238      * Splits the specified URI into a namespace and a local name and returns\r
239      * them both separately as an array.\r
240      * \r
241      * @param uri the URI to split, must be non-null\r
242      * @return [0] = namespace, [1] = local name\r
243      * @throws IllegalArgumentException for URIs without a\r
244      *         {@link #NAMESPACE_LOCAL_SEPARATOR}\r
245      * @throws NullPointerException for <code>null</code> URIs\r
246      */\r
247     public static String[] splitNamespaceAndLocalName(String uri) {\r
248         if (uri == null)\r
249             throw new NullPointerException("null uri");\r
250         int separatorIndex = assertSingleSeparatorPosition(uri);\r
251         return new String[] { uri.substring(0, separatorIndex), uri.substring(separatorIndex + 1) };\r
252     }\r
253 \r
254     /**\r
255      * Converts a unicode string into an RFC 2396 compliant URI, using %NN\r
256      * escapes where appropriate, including the\r
257      * {@link #NAMESPACE_PATH_SEPARATOR} character.\r
258      * \r
259      * @param localName the string to escape\r
260      * @return the escaped string\r
261      * @throws NullPointerException for <code>null</code> URIs\r
262      */\r
263     public static String escapeURI(String localName) {\r
264         if (localName == null)\r
265             throw new NullPointerException("null local name");\r
266         String result = encode(localName);\r
267         return result;\r
268     }\r
269 \r
270     /**\r
271      * Add a suffix path to a namespace string, i.e. join the strings to\r
272      * together with the {@link #NAMESPACE_PATH_SEPARATOR} character in between.\r
273      * \r
274      * @param namespace the namespace to append to\r
275      * @param suffix the suffix to append\r
276      * @return the joined namespace\r
277      */\r
278     public static String appendURINamespace(String namespace, String suffix) {\r
279         //return namespace + NAMESPACE_PATH_SEPARATOR + suffix;\r
280         return new StringBuffer(namespace.length() + 1 + suffix.length())\r
281         .append(namespace)\r
282         .append(NAMESPACE_PATH_SEPARATOR)\r
283         .append(suffix)\r
284         .toString();\r
285     }\r
286 \r
287     /**\r
288      * Join a namespace and a localname to form an URI with\r
289      * {@link #NAMESPACE_LOCAL_SEPARATOR}.\r
290      * \r
291      * @param namespace the namespace part to join\r
292      * @param localName the localname part to join\r
293      * @return the joined URI\r
294      */\r
295     public static String makeURI(String namespace, String localName) {\r
296         //return namespace + NAMESPACE_LOCAL_SEPARATOR + escapeURI(localName);\r
297         String escapedLocalName = escapeURI(localName);\r
298         return new StringBuffer(namespace.length() + 1 + escapedLocalName.length())\r
299         .append(namespace)\r
300         .append(NAMESPACE_LOCAL_SEPARATOR)\r
301         .append(escapedLocalName)\r
302         .toString();\r
303     }\r
304 \r
305     /**\r
306      * Convert a Unicode string, first to UTF-8 and then to an RFC 2396\r
307      * compliant URI with optional fragment identifier using %NN escape\r
308      * mechanism as appropriate. The '%' character is assumed to already\r
309      * indicated an escape byte. The '%' character must be followed by two\r
310      * hexadecimal digits.\r
311      * \r
312      * <p>\r
313      * Meant to be used for encoding URI local name parts if it is desired to\r
314      * have '/' characters in the local name without creating a new namespace.\r
315      * For example these two URI's:<br/>\r
316      * \r
317      * <code>\r
318      * http://foo.bar.com/foo/bar/org%2Fcom<br/>\r
319      * http://foo.bar.com/foo/bar/net%2Fcom<br/>\r
320      * </code>\r
321      * \r
322      * have the same namespace <code>http://foo.bar.com/foo/bar/</code> and\r
323      * different local names <code>org%2Fcom</code> and <code>net%2Fcom</code>\r
324      * or <code>org/com</code> and <code>net/com</code> in unescaped form.\r
325      * </p>\r
326      * \r
327      * @param unicode The uri, in characters specified by RFC 2396 + '#'\r
328      * @return The corresponding Unicode String\r
329      */\r
330     public static String escape(String unicode) {\r
331         return encode(unicode);\r
332     }\r
333 \r
334 \r
335     final private static Charset UTF8 = Charset.forName("UTF-8");\r
336     final private static Charset ASCII = Charset.forName("US-ASCII");\r
337 \r
338     /* Copied and modified from Jena 2.4 com.hp.hpl.jena.util.URIref */\r
339     private static String encode(String unicode) {\r
340         boolean needsEscapes = needsEscaping(unicode);\r
341         if (!needsEscapes)\r
342             return unicode;\r
343 \r
344         byte utf8[] = unicode.getBytes(UTF8);\r
345         byte rsltAscii[] = new byte[utf8.length * 6];\r
346         int in = 0;\r
347         int out = 0;\r
348         while (in < utf8.length) {\r
349             switch (utf8[in]) {\r
350                 case (byte)'a': case (byte)'b': case (byte)'c': case (byte)'d': case (byte)'e': case (byte)'f': case (byte)'g': case (byte)'h': case (byte)'i': case (byte)'j': case (byte)'k': case (byte)'l': case (byte)'m': case (byte)'n': case (byte)'o': case (byte)'p': case (byte)'q': case (byte)'r': case (byte)'s': case (byte)'t': case (byte)'u': case (byte)'v': case (byte)'w': case (byte)'x': case (byte)'y': case (byte)'z':\r
351                 case (byte)'A': case (byte)'B': case (byte)'C': case (byte)'D': case (byte)'E': case (byte)'F': case (byte)'G': case (byte)'H': case (byte)'I': case (byte)'J': case (byte)'K': case (byte)'L': case (byte)'M': case (byte)'N': case (byte)'O': case (byte)'P': case (byte)'Q': case (byte)'R': case (byte)'S': case (byte)'T': case (byte)'U': case (byte)'V': case (byte)'W': case (byte)'X': case (byte)'Y': case (byte)'Z':\r
352                 case (byte)'0': case (byte)'1': case (byte)'2': case (byte)'3': case (byte)'4': case (byte)'5': case (byte)'6': case (byte)'7': case (byte)'8': case (byte)'9':\r
353                 case (byte)';': case (byte)'?': case (byte)':': case (byte)'@': case (byte)'=': case (byte)'+': case (byte)'$': case (byte)',':\r
354                 case (byte)'-': case (byte)'_': case (byte)'.': case (byte)'!': case (byte)'~': case (byte)'*': case (byte)'\'': case (byte)'(': case (byte)')':\r
355                 case (byte)'[': case (byte)']':\r
356                     rsltAscii[out] = utf8[in];\r
357                     out++;\r
358                     in++;\r
359                     break;\r
360                 case (byte)' ':\r
361                     rsltAscii[out++] = (byte) '%';\r
362                     rsltAscii[out++] = '2';\r
363                     rsltAscii[out++] = '0';\r
364                     in++;\r
365                     break;\r
366                 case (byte) '%':\r
367                     // [lehtonen] NOTE: all input needs to be escaped, i.e. "%01" should result in "%2501", not "%01".\r
368                     // escape+unescape is a bijection, not an idempotent operation. \r
369                     // Fall through to to escape '%' as '%25'\r
370                 case (byte) '#':\r
371                 case (byte) '/':\r
372                     // Fall through to escape '/'\r
373                 case (byte)'&':\r
374                     // Fall through to escape '&' characters to avoid them\r
375                     // being interpreted as SGML entities.\r
376                 default:\r
377                     rsltAscii[out++] = (byte) '%';\r
378                     // Get rid of sign ...\r
379                     int c = (utf8[in]) & 255;\r
380                     rsltAscii[out++] = hexEncode(c / 16);\r
381                     rsltAscii[out++] = hexEncode(c % 16);\r
382                     in++;\r
383                     break;\r
384             }\r
385         }\r
386         return new String(rsltAscii, 0, out, ASCII);\r
387     }\r
388 \r
389     /*\r
390      * RFC 3986 section 2.2 Reserved Characters (January 2005)\r
391      * !*'();:@&=+$,/?#[]\r
392      */\r
393     private static boolean needsEscaping(String unicode) {\r
394         int len = unicode.length();\r
395         for (int i = 0; i < len; ++i) {\r
396             switch (unicode.charAt(i)) {\r
397                 case (byte)'!':\r
398                 case (byte)'*':\r
399                 case (byte)'\'':\r
400                 case (byte)'(':\r
401                 case (byte)')':\r
402                 case (byte)';':\r
403                 case (byte)':':\r
404                 case (byte)'@':\r
405                 case (byte)'=': \r
406                 case (byte)'+':\r
407                 case (byte)'$':\r
408                 case (byte)',':\r
409                 case (byte)'?':\r
410                 case (byte)'~':\r
411                 case (byte)'[':\r
412                 case (byte)']':\r
413                     break;\r
414                 case (byte)' ':\r
415                 case (byte) '#':\r
416                 case (byte) '%':\r
417                 case (byte) '/':\r
418                 case (byte)'&':\r
419                     return true;\r
420             }\r
421         }\r
422         return false;\r
423     }\r
424 \r
425     private static boolean needsUnescaping(String unicode) {\r
426         return unicode.indexOf('%') > -1;\r
427     }\r
428 \r
429     /**\r
430      * Convert a URI, in US-ASCII, with escaped characters taken from UTF-8, to\r
431      * the corresponding Unicode string. On ill-formed input the results are\r
432      * undefined, specifically if the unescaped version is not a UTF-8 String,\r
433      * some String will be returned.\r
434      * \r
435      * @param uri the uri, in characters specified by RFC 2396 + '#'.\r
436      * @return the corresponding Unicode String.\r
437      * @exception IllegalArgumentException if a % hex sequence is ill-formed.\r
438      */\r
439     public static String unescape(String uri) {\r
440         try {\r
441             if (!needsUnescaping(uri))\r
442                 return uri;\r
443 \r
444             byte ascii[] = uri.getBytes("US-ASCII");\r
445             byte utf8[] = new byte[ascii.length];\r
446             int in = 0;\r
447             int out = 0;\r
448             while ( in < ascii.length ) {\r
449                 if (ascii[in] == (byte) '%') {\r
450                     in++;\r
451                     utf8[out++] = (byte) (hexDecode(ascii[in]) * 16 | hexDecode(ascii[in + 1]));\r
452                     in += 2;\r
453                 } else {\r
454                     utf8[out++] = ascii[in++];\r
455                 }\r
456             }\r
457             return new String(utf8, 0, out, "UTF-8");\r
458         } catch (IllegalArgumentException e) {\r
459             throw new IllegalArgumentException("Problem while unescaping string: " + uri, e);\r
460         } catch (java.io.UnsupportedEncodingException e) {\r
461             throw new Error("The JVM is required to support UTF-8 and US-ASCII encodings.");\r
462         } catch (ArrayIndexOutOfBoundsException ee) {\r
463             throw new IllegalArgumentException("Incomplete Hex escape sequence in " + uri);\r
464         }\r
465     }\r
466 \r
467     /* Copied from Jena 2.4 com.hp.hpl.jena.util.URIref */\r
468     private static byte hexEncode(int i) {\r
469         if (i < 10)\r
470             return (byte) ('0' + i);\r
471         else\r
472             return (byte)('A' + i - 10);\r
473     }\r
474 \r
475     /* Copied from Jena 2.4 com.hp.hpl.jena.util.URIref */\r
476     private static int hexDecode(byte b) {\r
477         switch (b) {\r
478             case (byte)'a': case (byte)'b': case (byte)'c': case (byte)'d': case (byte)'e': case (byte)'f':\r
479                 return ((b) & 255) - 'a' + 10;\r
480             case (byte)'A': case (byte)'B': case (byte)'C': case (byte)'D': case (byte)'E': case (byte)'F':\r
481                 return b - (byte) 'A' + 10;\r
482             case (byte)'0': case (byte)'1': case (byte)'2': case (byte)'3': case (byte)'4': case (byte)'5': case (byte)'6': case (byte)'7': case (byte)'8': case (byte)'9':\r
483                 return b - (byte) '0';\r
484             default:\r
485                 throw new IllegalArgumentException("Bad Hex escape character: " + ((b)&255) );\r
486         }\r
487     }\r
488 \r
489     /**\r
490      * Some simple tests.\r
491      * @param args\r
492      */\r
493     public static void main(String[] args) {\r
494         String s;\r
495         s = "http://www.vtt.fi%2FSome- %25 Namespace/Jotain";\r
496         System.out.println(String.format("escape+unescape: %s -> %s -> %s", s, escape(s), unescape(escape(s))));\r
497         s = "http://www.vtt.fi%2FPSK";\r
498         System.out.println(String.format("unescape: %s -> %s", s, unescape(s)));\r
499         s = "http://www.vtt.fi%2FSome-Namespace/Jotain / Muuta";\r
500         System.out.println(String.format("escape: %s -> %s", s, escape(s)));\r
501         s = "Jotain / Muuta";\r
502         System.out.println(String.format("escape: %s -> %s", s, escape(s)));\r
503 \r
504         System.out.println("escapeURI: " + escapeURI("foo/bar/org%2Fnet"));\r
505         System.out.println("escapeURI('...#...'): " + escapeURI("foo/bar#org%2Fnet"));\r
506         s = makeURI("http://foo.bar.com/foo/bar", "baz/guuk/org%2Fnet");\r
507         System.out.println("escapeURI: " + s);\r
508         System.out.println("getNamespace: " + getNamespace(s));\r
509         System.out.println("getLocalName: " + getLocalName(s));\r
510 \r
511         testEscape("/", "%2F");\r
512         testEscape("#", "%23");\r
513         testEscape("%", "%25");\r
514         testEscape("%01", "%2501");\r
515         testEscape("%GG", "%25GG");\r
516     }\r
517 \r
518     private static void testEscape(String unescaped, String expectedEscaped) {\r
519         String esc = escape(unescaped);\r
520         String unesc = unescape(esc);\r
521         System.out.format("escape('%s')='%s', unescape('%s')='%s'\n", unescaped, esc, esc, unesc);\r
522         if (!esc.equals(expectedEscaped))\r
523             throw new AssertionError("escape('" + unescaped + "') was expected to return '" + expectedEscaped + "' but returned '" + esc + "'");\r
524         if (!unesc.equals(unescaped))\r
525             throw new AssertionError("unescape(escape('" + unescaped + "'))=unescape(" + esc + ") was expected to return '" + unescaped + "' but returned '" + unesc + "'");\r
526     }\r
527 \r
528 }\r