You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2011/01/21 11:26:18 UTC

svn commit: r1061721 - in /karaf/trunk: manual/src/main/webapp/developers-guide/ manual/src/main/webapp/users-guide/ shell/osgi/ shell/osgi/src/main/java/org/apache/karaf/shell/osgi/ util/src/main/java/org/apache/karaf/util/

Author: jbonofre
Date: Fri Jan 21 10:26:18 2011
New Revision: 1061721

URL: http://svn.apache.org/viewvc?rev=1061721&view=rev
Log:
[KARAF-403] Add support of escaped ASCII character and document how to create bundle including OSGI-INF/bundle.info entry.

Added:
    karaf/trunk/util/src/main/java/org/apache/karaf/util/StringEscapeUtils.java
Modified:
    karaf/trunk/manual/src/main/webapp/developers-guide/creating-bundles.conf
    karaf/trunk/manual/src/main/webapp/users-guide/using-console.conf
    karaf/trunk/shell/osgi/pom.xml
    karaf/trunk/shell/osgi/src/main/java/org/apache/karaf/shell/osgi/Info.java
    karaf/trunk/util/src/main/java/org/apache/karaf/util/Properties.java

Modified: karaf/trunk/manual/src/main/webapp/developers-guide/creating-bundles.conf
URL: http://svn.apache.org/viewvc/karaf/trunk/manual/src/main/webapp/developers-guide/creating-bundles.conf?rev=1061721&r1=1061720&r2=1061721&view=diff
==============================================================================
--- karaf/trunk/manual/src/main/webapp/developers-guide/creating-bundles.conf (original)
+++ karaf/trunk/manual/src/main/webapp/developers-guide/creating-bundles.conf Fri Jan 21 10:26:18 2011
@@ -1,3 +1,73 @@
+h1. Add extended information to bundles
+
+Karaf supports a OSGI-INF/bundle.info file in a bundle.
+
+This file is extended description of the bundle. It supports ASCII character (to color, formatting, etc).
+
+For instance, you can define a bundle like this (using Apache Felix maven-bundle-plugin):
+
+{code:lang=xml}
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>my.groupId</groupId>
+    <artifactId>my.bundle</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <name>My Bundle</name>
+    <description>My bundle short description</description>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.2.0</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+                        ...
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
+{code}
+
+And simply add src/main/resources/OSGI-INF/bundle.info file containing, for instance:
+
+{code}
+\u001B[1mSYNOPSIS\u001B[0m
+    ${project.description}
+
+\u001B[1mDESCRIPTION\u001B[0m
+    Long description of your bundle, including usage, etc.
+
+\u001B[1mSEE ALSO\u001B[0m
+    \u001B[36mhttp://yourside\u001B[0m
+    \u001B[36mhttp://yourside/docs\u001B[0m
+
+{code}
+
+You can display this extended information using:
+
+{code}
+root@karaf> osgi:info
+{code}
+
 h1. Creating bundles for third party dependencies
 
 Karaf support the wrap: protocol execution.

Modified: karaf/trunk/manual/src/main/webapp/users-guide/using-console.conf
URL: http://svn.apache.org/viewvc/karaf/trunk/manual/src/main/webapp/users-guide/using-console.conf?rev=1061721&r1=1061720&r2=1061721&view=diff
==============================================================================
--- karaf/trunk/manual/src/main/webapp/users-guide/using-console.conf (original)
+++ karaf/trunk/manual/src/main/webapp/users-guide/using-console.conf Fri Jan 21 10:26:18 2011
@@ -5,47 +5,71 @@ h2. Viewving available commands
 To see a list of the available commands in the console press the {{<tab>}} key at the prompt.
 
 {code}
-root@karaf> <tab>
-*:help                      addurl                      admin:change-port           admin:connect
-admin:create                admin:destroy               admin:list                  admin:start
-admin:stop                  bundle-level                cancel                      cat
-change-port                 clear                       config:cancel               config:edit
-config:list                 config:propappend           config:propdel              config:proplist
-config:propset              config:update               connect                     create
-destroy                     dev:dynamic-import          dev:framework               dev:print-stack-traces
-dev:restart                 dev:show-tree               display                     display-exception
-dynamic-import              each                        echo                        edit
-exec                        exports                     features:addurl             features:info
-features:install            features:list               features:listrepositories   features:listurl
-features:listversions       features:refreshurl         features:removerepository   features:removeurl
-features:uninstall          framework                   get                         grep
-headers                     help                        history                     if
-imports                     info                        install                     java
-list                        listrepositories            listurl                     listversions
-log:display                 log:display-exception       log:get                     log:set
-logout                      ls                          new                         osgi:bundle-level
-osgi:headers                osgi:install                osgi:list                   osgi:ls
-osgi:refresh                osgi:resolve                osgi:restart                osgi:shutdown
-osgi:start                  osgi:start-level            osgi:stop                   osgi:uninstall
-osgi:update                 packages:exports            packages:imports            print-stack-traces
-printf                      propappend                  propdel                     proplist
-propset                     refresh                     refreshurl                  removerepository
-removeurl                   resolve                     restart                     set
-shell:cat                   shell:clear                 shell:each                  shell:echo
-shell:exec                  shell:grep                  shell:history               shell:if
-shell:info                  shell:java                  shell:logout                shell:new
-shell:printf                shell:sleep                 shell:sort                  shell:tac
-show-tree                   shutdown                    sleep                       sort
-ssh                         ssh:ssh                     ssh:sshd                    sshd
-start                       start-level                 stop                        tac
-uninstall                   update
+root@root> <tab>Display all 182 possibilities? (y or n)
+*:help                           addurl                           admin:change-opts
+admin:change-rmi-registry-port   admin:change-ssh-port            admin:connect
+admin:create                     admin:destroy                    admin:list
+admin:rename                     admin:start                      admin:stop
+bundle-level                     cancel                           cat
+change-opts                      change-rmi-registry-port         change-ssh-port
+clear                            commandlist                      config:cancel
+config:edit                      config:list                      config:propappend
+config:propdel                   config:proplist                  config:propset
+config:update                    connect                          create
+create-dump                      destroy                          dev:create-dump
+dev:dynamic-import               dev:framework                    dev:print-stack-traces
+dev:restart                      dev:show-tree                    dev:watch
+display                          display-exception                dynamic-import
+each                             echo                             edit
+exec                             exports                          features:addurl
+features:info                    features:install                 features:list
+features:listrepositories        features:listurl                 features:listversions
+features:refreshurl              features:removerepository        features:removeurl
+features:uninstall               framework                        get
+grep                             head                             headers
+help                             history                          if
+imports                          info                             install
+jaas:cancel                      jaas:commandlist                 jaas:list
+jaas:manage                      jaas:roleadd                     jaas:roledel
+jaas:update                      jaas:useradd                     jaas:userdel
+jaas:userlist                    java                             list
+listrepositories                 listurl                          listversions
+log:clear                        log:display                      log:display-exception
+log:get                          log:set                          log:tail
+logout                           ls                               manage
+more                             new                              osgi:bundle-level
+osgi:headers                     osgi:info                        osgi:install
+osgi:list                        osgi:ls                          osgi:refresh
+osgi:resolve                     osgi:restart                     osgi:shutdown
+osgi:start                       osgi:start-level                 osgi:stop
+osgi:uninstall                   osgi:update                      packages:exports
+packages:imports                 print-stack-traces               printf
+propappend                       propdel                          proplist
+propset                          refresh                          refreshurl
+removerepository                 removeurl                        rename
+resolve                          restart                          roleadd
+roledel                          set                              shell:cat
+shell:clear                      shell:each                       shell:echo
+shell:exec                       shell:grep                       shell:head
+shell:history                    shell:if                         shell:info
+shell:java                       shell:logout                     shell:more
+shell:new                        shell:printf                     shell:sleep
+shell:sort                       shell:tac                        shell:tail
+show-tree                        shutdown                         sleep
+sort                             ssh                              ssh:ssh
+ssh:sshd                         sshd                             start
+start-level                      stop                             tac
+tail                             uninstall                        update
+useradd                          userdel                          userlist
+watch
+root@root>
 {code}
 
 The {{<tab>}} key toggles completion anywhere on the line, so if you want to see the commands in the {{osgi}} group, type the first letters and hit {{<tab>}}.  Depending on the commands, completion may be available on options and arguments too.
 
 h2. Getting help on a command
 
-To view help on a particulare command, type the command followed by {{--help}} or use the {{help}} command followed by the name of the command:
+To view help on a particular command, type the command followed by {{--help}} or use the {{help}} command followed by the name of the command:
 
 {code}
 karaf@root> features:list --help

Modified: karaf/trunk/shell/osgi/pom.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/osgi/pom.xml?rev=1061721&r1=1061720&r2=1061721&view=diff
==============================================================================
--- karaf/trunk/shell/osgi/pom.xml (original)
+++ karaf/trunk/shell/osgi/pom.xml Fri Jan 21 10:26:18 2011
@@ -44,6 +44,10 @@
             <groupId>org.apache.karaf.shell</groupId>
             <artifactId>org.apache.karaf.shell.console</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.karaf</groupId>
+            <artifactId>org.apache.karaf.util</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.osgi</groupId>
@@ -91,6 +95,7 @@
                             org.springframework.*
                         </DynamicImport-Package>
                         <Private-Package>
+                            org.apache.karaf.util,
                             org.apache.felix.utils.version,
                             org.apache.felix.utils.manifest,
                             !*

Modified: karaf/trunk/shell/osgi/src/main/java/org/apache/karaf/shell/osgi/Info.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/osgi/src/main/java/org/apache/karaf/shell/osgi/Info.java?rev=1061721&r1=1061720&r2=1061721&view=diff
==============================================================================
--- karaf/trunk/shell/osgi/src/main/java/org/apache/karaf/shell/osgi/Info.java (original)
+++ karaf/trunk/shell/osgi/src/main/java/org/apache/karaf/shell/osgi/Info.java Fri Jan 21 10:26:18 2011
@@ -19,7 +19,7 @@ package org.apache.karaf.shell.osgi;
 import org.apache.felix.gogo.commands.Argument;
 import org.apache.felix.gogo.commands.Command;
 import org.apache.karaf.shell.console.OsgiCommandSupport;
-import org.fusesource.jansi.Ansi;
+import org.apache.karaf.util.StringEscapeUtils;
 import org.osgi.framework.Bundle;
 
 import java.io.BufferedReader;
@@ -69,7 +69,7 @@ public class Info extends OsgiCommandSup
                 BufferedReader reader = new BufferedReader(new InputStreamReader(bundleInfo.openStream()));
                 String line;
                 while ((line = reader.readLine()) != null) {
-                    System.out.println(line);
+                    System.out.println(StringEscapeUtils.unescapeJava(line));
                 }
                 reader.close();
             } catch (Exception e) {

Modified: karaf/trunk/util/src/main/java/org/apache/karaf/util/Properties.java
URL: http://svn.apache.org/viewvc/karaf/trunk/util/src/main/java/org/apache/karaf/util/Properties.java?rev=1061721&r1=1061720&r2=1061721&view=diff
==============================================================================
--- karaf/trunk/util/src/main/java/org/apache/karaf/util/Properties.java (original)
+++ karaf/trunk/util/src/main/java/org/apache/karaf/util/Properties.java Fri Jan 21 10:26:18 2011
@@ -66,12 +66,6 @@ public class Properties extends Abstract
     /** Constant for the platform specific line separator.*/
     private static final String LINE_SEPARATOR = System.getProperty("line.separator");
 
-    /** Constant for the radix of hex numbers.*/
-    private static final int HEX_RADIX = 16;
-
-    /** Constant for the length of a unicode literal.*/
-    private static final int UNICODE_LEN = 4;
-
     private Map<String,String> storage = new LinkedHashMap<String,String>();
     private Map<String,Layout> layout = new LinkedHashMap<String,Layout>();
     private List<String> header;
@@ -326,196 +320,6 @@ public class Properties extends Abstract
     }
 
     /**
-     * <p>Unescapes any Java literals found in the <code>String</code> to a
-     * <code>Writer</code>.</p> This is a slightly modified version of the
-     * StringEscapeUtils.unescapeJava() function in commons-lang that doesn't
-     * drop escaped separators (i.e '\,').
-     *
-     * @param str  the <code>String</code> to unescape, may be null
-     * @return the processed string
-     * @throws IllegalArgumentException if the Writer is <code>null</code>
-     */
-    protected static String unescapeJava(String str) {
-        if (str == null) {
-            return null;
-        }
-        int sz = str.length();
-        StringBuffer out = new StringBuffer(sz);
-        StringBuffer unicode = new StringBuffer(UNICODE_LEN);
-        boolean hadSlash = false;
-        boolean inUnicode = false;
-        for (int i = 0; i < sz; i++) {
-            char ch = str.charAt(i);
-            if (inUnicode) {
-                // if in unicode, then we're reading unicode
-                // values in somehow
-                unicode.append(ch);
-                if (unicode.length() == UNICODE_LEN) {
-                    // unicode now contains the four hex digits
-                    // which represents our unicode character
-                    try {
-                        int value = Integer.parseInt(unicode.toString(), HEX_RADIX);
-                        out.append((char) value);
-                        unicode.setLength(0);
-                        inUnicode = false;
-                        hadSlash = false;
-                    } catch (NumberFormatException nfe) {
-                        throw new IllegalArgumentException("Unable to parse unicode value: " + unicode, nfe);
-                    }
-                }
-                continue;
-            }
-
-            if (hadSlash) {
-                // handle an escaped value
-                hadSlash = false;
-                switch (ch) {
-                    case '\\' :
-                        out.append('\\');
-                        break;
-                    case '\'' :
-                        out.append('\'');
-                        break;
-                    case '\"' :
-                        out.append('"');
-                        break;
-                    case 'r' :
-                        out.append('\r');
-                        break;
-                    case 'f' :
-                        out.append('\f');
-                        break;
-                    case 't' :
-                        out.append('\t');
-                        break;
-                    case 'n' :
-                        out.append('\n');
-                        break;
-                    case 'b' :
-                        out.append('\b');
-                        break;
-                    case 'u' :
-                        // uh-oh, we're in unicode country....
-                        inUnicode = true;
-                        break;
-                    default :
-                        out.append(ch);
-                        break;
-                }
-                continue;
-            } else if (ch == '\\') {
-                hadSlash = true;
-                continue;
-            }
-            out.append(ch);
-        }
-
-        if (hadSlash) {
-            // then we're in the weird case of a \ at the end of the
-            // string, let's output it anyway.
-            out.append('\\');
-        }
-
-        return out.toString();
-    }
-
-    /**
-     * <p>Escapes the characters in a <code>String</code> using Java String rules.</p>
-     *
-     * <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
-     *
-     * <p>So a tab becomes the characters <code>'\\'</code> and
-     * <code>'t'</code>.</p>
-     *
-     * <p>The only difference between Java strings and JavaScript strings
-     * is that in JavaScript, a single quote must be escaped.</p>
-     *
-     * <p>Example:
-     * <pre>
-     * input string: He didn't say, "Stop!"
-     * output string: He didn't say, \"Stop!\"
-     * </pre>
-     * </p>
-     *
-     * @param str  String to escape values in, may be null
-     * @return String with escaped values, <code>null</code> if null string input
-     */
-    protected static String escapeJava(String str) {
-        if (str == null) {
-            return null;
-        }
-        int sz = str.length();
-        StringBuffer out = new StringBuffer(sz * 2);
-        for (int i = 0; i < sz; i++) {
-            char ch = str.charAt(i);
-            // handle unicode
-            if (ch > 0xfff) {
-                out.append("\\u").append(hex(ch));
-            } else if (ch > 0xff) {
-                out.append("\\u0").append(hex(ch));
-            } else if (ch > 0x7f) {
-                out.append("\\u00").append(hex(ch));
-            } else if (ch < 32) {
-                switch (ch) {
-                    case '\b' :
-                        out.append('\\');
-                        out.append('b');
-                        break;
-                    case '\n' :
-                        out.append('\\');
-                        out.append('n');
-                        break;
-                    case '\t' :
-                        out.append('\\');
-                        out.append('t');
-                        break;
-                    case '\f' :
-                        out.append('\\');
-                        out.append('f');
-                        break;
-                    case '\r' :
-                        out.append('\\');
-                        out.append('r');
-                        break;
-                    default :
-                        if (ch > 0xf) {
-                            out.append("\\u00").append(hex(ch));
-                        } else {
-                            out.append("\\u000").append(hex(ch));
-                        }
-                        break;
-                }
-            } else {
-                switch (ch) {
-                    case '"' :
-                        out.append('\\');
-                        out.append('"');
-                        break;
-                    case '\\' :
-                        out.append('\\');
-                        out.append('\\');
-                        break;
-                    default :
-                        out.append(ch);
-                        break;
-                }
-            }
-        }
-        return out.toString();
-    }
-
-    /**
-     * <p>Returns an upper case hexadecimal <code>String</code> for the given
-     * character.</p>
-     *
-     * @param ch The character to convert.
-     * @return An upper case hexadecimal <code>String</code>
-     */
-    protected static String hex(char ch) {
-        return Integer.toHexString(ch).toUpperCase(Locale.ENGLISH);
-    }
-
-    /**
      * <p>Checks if the value is in the given array.</p>
      *
      * <p>The method returns <code>false</code> if a <code>null</code> array is passed in.</p>
@@ -783,8 +587,8 @@ public class Properties extends Abstract
 
             // parse the line
             String[] property = parseProperty(line);
-            propertyName = unescapeJava(property[0]);
-            propertyValue = unescapeJava(property[1]);
+            propertyName = StringEscapeUtils.unescapeJava(property[0]);
+            propertyValue = StringEscapeUtils.unescapeJava(property[1]);
             return true;
         }
 
@@ -981,7 +785,7 @@ public class Properties extends Abstract
         {
             write(escapeKey(key));
             write(" = ");
-            write(escapeJava(value));
+            write(StringEscapeUtils.escapeJava(value));
             writeln(null);
         }
 

Added: karaf/trunk/util/src/main/java/org/apache/karaf/util/StringEscapeUtils.java
URL: http://svn.apache.org/viewvc/karaf/trunk/util/src/main/java/org/apache/karaf/util/StringEscapeUtils.java?rev=1061721&view=auto
==============================================================================
--- karaf/trunk/util/src/main/java/org/apache/karaf/util/StringEscapeUtils.java (added)
+++ karaf/trunk/util/src/main/java/org/apache/karaf/util/StringEscapeUtils.java Fri Jan 21 10:26:18 2011
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.util;
+
+import java.util.Locale;
+
+/**
+ * <p>
+ * Util class to manipulate String, especially around escape/unescape.
+ * </p>
+ *
+ * @author gnodet, jbonofre
+ */
+public class StringEscapeUtils {
+
+    /** Constant for the radix of hex numbers.*/
+    private static final int HEX_RADIX = 16;
+
+    /** Constant for the length of a unicode literal.*/
+    private static final int UNICODE_LEN = 4;
+
+    /**
+     * <p>Unescapes any Java literals found in the <code>String</code> to a
+     * <code>Writer</code>.</p> This is a slightly modified version of the
+     * StringEscapeUtils.unescapeJava() function in commons-lang that doesn't
+     * drop escaped separators (i.e '\,').
+     *
+     * @param str  the <code>String</code> to unescape, may be null
+     * @return the processed string
+     * @throws IllegalArgumentException if the Writer is <code>null</code>
+     */
+    public static String unescapeJava(String str) {
+        if (str == null) {
+            return null;
+        }
+        int sz = str.length();
+        StringBuffer out = new StringBuffer(sz);
+        StringBuffer unicode = new StringBuffer(UNICODE_LEN);
+        boolean hadSlash = false;
+        boolean inUnicode = false;
+        for (int i = 0; i < sz; i++) {
+            char ch = str.charAt(i);
+            if (inUnicode) {
+                // if in unicode, then we're reading unicode
+                // values in somehow
+                unicode.append(ch);
+                if (unicode.length() == UNICODE_LEN) {
+                    // unicode now contains the four hex digits
+                    // which represents our unicode character
+                    try {
+                        int value = Integer.parseInt(unicode.toString(), HEX_RADIX);
+                        out.append((char) value);
+                        unicode.setLength(0);
+                        inUnicode = false;
+                        hadSlash = false;
+                    } catch (NumberFormatException nfe) {
+                        throw new IllegalArgumentException("Unable to parse unicode value: " + unicode, nfe);
+                    }
+                }
+                continue;
+            }
+
+            if (hadSlash) {
+                // handle an escaped value
+                hadSlash = false;
+                switch (ch) {
+                    case '\\' :
+                        out.append('\\');
+                        break;
+                    case '\'' :
+                        out.append('\'');
+                        break;
+                    case '\"' :
+                        out.append('"');
+                        break;
+                    case 'r' :
+                        out.append('\r');
+                        break;
+                    case 'f' :
+                        out.append('\f');
+                        break;
+                    case 't' :
+                        out.append('\t');
+                        break;
+                    case 'n' :
+                        out.append('\n');
+                        break;
+                    case 'b' :
+                        out.append('\b');
+                        break;
+                    case 'u' :
+                        // uh-oh, we're in unicode country....
+                        inUnicode = true;
+                        break;
+                    default :
+                        out.append(ch);
+                        break;
+                }
+                continue;
+            } else if (ch == '\\') {
+                hadSlash = true;
+                continue;
+            }
+            out.append(ch);
+        }
+
+        if (hadSlash) {
+            // then we're in the weird case of a \ at the end of the
+            // string, let's output it anyway.
+            out.append('\\');
+        }
+
+        return out.toString();
+    }
+
+    /**
+     * <p>Escapes the characters in a <code>String</code> using Java String rules.</p>
+     *
+     * <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
+     *
+     * <p>So a tab becomes the characters <code>'\\'</code> and
+     * <code>'t'</code>.</p>
+     *
+     * <p>The only difference between Java strings and JavaScript strings
+     * is that in JavaScript, a single quote must be escaped.</p>
+     *
+     * <p>Example:
+     * <pre>
+     * input string: He didn't say, "Stop!"
+     * output string: He didn't say, \"Stop!\"
+     * </pre>
+     * </p>
+     *
+     * @param str  String to escape values in, may be null
+     * @return String with escaped values, <code>null</code> if null string input
+     */
+    public static String escapeJava(String str) {
+        if (str == null) {
+            return null;
+        }
+        int sz = str.length();
+        StringBuffer out = new StringBuffer(sz * 2);
+        for (int i = 0; i < sz; i++) {
+            char ch = str.charAt(i);
+            // handle unicode
+            if (ch > 0xfff) {
+                out.append("\\u").append(hex(ch));
+            } else if (ch > 0xff) {
+                out.append("\\u0").append(hex(ch));
+            } else if (ch > 0x7f) {
+                out.append("\\u00").append(hex(ch));
+            } else if (ch < 32) {
+                switch (ch) {
+                    case '\b' :
+                        out.append('\\');
+                        out.append('b');
+                        break;
+                    case '\n' :
+                        out.append('\\');
+                        out.append('n');
+                        break;
+                    case '\t' :
+                        out.append('\\');
+                        out.append('t');
+                        break;
+                    case '\f' :
+                        out.append('\\');
+                        out.append('f');
+                        break;
+                    case '\r' :
+                        out.append('\\');
+                        out.append('r');
+                        break;
+                    default :
+                        if (ch > 0xf) {
+                            out.append("\\u00").append(hex(ch));
+                        } else {
+                            out.append("\\u000").append(hex(ch));
+                        }
+                        break;
+                }
+            } else {
+                switch (ch) {
+                    case '"' :
+                        out.append('\\');
+                        out.append('"');
+                        break;
+                    case '\\' :
+                        out.append('\\');
+                        out.append('\\');
+                        break;
+                    default :
+                        out.append(ch);
+                        break;
+                }
+            }
+        }
+        return out.toString();
+    }
+
+    /**
+     * <p>Returns an upper case hexadecimal <code>String</code> for the given
+     * character.</p>
+     *
+     * @param ch The character to convert.
+     * @return An upper case hexadecimal <code>String</code>
+     */
+    public static String hex(char ch) {
+        return Integer.toHexString(ch).toUpperCase(Locale.ENGLISH);
+    }
+
+}