| /******************************************************************************* |
| * Copyright (c) 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.bpel.ui.util; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| |
| /** |
| * Utility class for calculating default namespaces based on namespace templates from the preference store. |
| */ |
| public class NamespaceUtils |
| { |
| protected static char NS_SEPARATOR = '/'; |
| protected static char PACKAGE_SEPARATOR = '.'; |
| protected static String HEX_PREFIX = "%"; //$NON-NLS-1$ |
| protected static String CHARSET_UTF8 = "UTF-8"; //$NON-NLS-1$ |
| |
| // unsafe characters |
| public static final String SPACE = " "; //$NON-NLS-1$ |
| public static final String DOUBLE_QUOTE = "\""; //$NON-NLS-1$ |
| public static final String LESS_THAN = "<"; //$NON-NLS-1$ |
| public static final String GREATER_THAN = ">"; //$NON-NLS-1$ |
| public static final String POUND = "#"; //$NON-NLS-1$ |
| public static final String PERCENT = "%"; //$NON-NLS-1$ |
| public static final String LEFT_CURLY = "{"; //$NON-NLS-1$ |
| public static final String RIGHT_CURLY = "}"; //$NON-NLS-1$ |
| public static final String PIPE = "|"; //$NON-NLS-1$ |
| public static final String BACKSLASH = "\\"; //$NON-NLS-1$ |
| public static final String CARET = "^"; //$NON-NLS-1$ |
| public static final String TILDE = "~"; //$NON-NLS-1$ |
| public static final String LEFT_SQUARE = "["; //$NON-NLS-1$ |
| public static final String RIGHT_SQUARE = "]"; //$NON-NLS-1$ |
| public static final String GRAVE = "`"; //$NON-NLS-1$ |
| |
| // escaped characters |
| public static final String ESCAPED_SPACE = "%20"; //$NON-NLS-1$ |
| public static final String ESCAPED_DOUBLE_QUOTE = "%22"; //$NON-NLS-1$ |
| public static final String ESCAPED_LESS_THAN = "%3C"; //$NON-NLS-1$ |
| public static final String ESCAPED_GREATER_THAN = "%3E"; //$NON-NLS-1$ |
| public static final String ESCAPED_POUND = "%23"; //$NON-NLS-1$ |
| public static final String ESCAPED_PERCENT = "%25"; //$NON-NLS-1$ |
| public static final String ESCAPED_LEFT_CURLY = "%7B"; //$NON-NLS-1$ |
| public static final String ESCAPED_RIGHT_CURLY = "%7D"; //$NON-NLS-1$ |
| public static final String ESCAPED_PIPE = "%7C"; //$NON-NLS-1$ |
| public static final String ESCAPED_BACKSLASH = "%5C"; //$NON-NLS-1$ |
| public static final String ESCAPED_CARET = "%5E"; //$NON-NLS-1$ |
| public static final String ESCAPED_TILDE = "%7E"; //$NON-NLS-1$ |
| public static final String ESCAPED_LEFT_SQUARE = "%5B"; //$NON-NLS-1$ |
| public static final String ESCAPED_RIGHT_SQUARE = "%5D"; //$NON-NLS-1$ |
| public static final String ESCAPED_GRAVE = "%60"; //$NON-NLS-1$ |
| |
| protected static HashMap fCharToEscaped = new HashMap(15); |
| static |
| { |
| fCharToEscaped.put(SPACE, ESCAPED_SPACE); |
| fCharToEscaped.put(DOUBLE_QUOTE, ESCAPED_DOUBLE_QUOTE); |
| fCharToEscaped.put(LESS_THAN, ESCAPED_LESS_THAN); |
| fCharToEscaped.put(GREATER_THAN, ESCAPED_GREATER_THAN); |
| fCharToEscaped.put(POUND, ESCAPED_POUND); |
| fCharToEscaped.put(PERCENT, ESCAPED_PERCENT); |
| fCharToEscaped.put(LEFT_CURLY, ESCAPED_LEFT_CURLY); |
| fCharToEscaped.put(RIGHT_CURLY, ESCAPED_RIGHT_CURLY); |
| fCharToEscaped.put(PIPE, ESCAPED_PIPE); |
| fCharToEscaped.put(BACKSLASH, ESCAPED_BACKSLASH); |
| fCharToEscaped.put(CARET, ESCAPED_CARET); |
| fCharToEscaped.put(TILDE, ESCAPED_TILDE); |
| fCharToEscaped.put(LEFT_SQUARE, ESCAPED_LEFT_SQUARE); |
| fCharToEscaped.put(RIGHT_SQUARE, ESCAPED_RIGHT_SQUARE); |
| fCharToEscaped.put(GRAVE, ESCAPED_GRAVE); |
| } |
| |
| /** |
| * Converts a UTF-8 escaped string back into a unicode string. This method undoes the |
| * operation performed in convertNamespaceToUri. |
| * |
| * @param uri |
| * @return - a unicode representation of the given UTF-8 escaped string. |
| * @see convertNamespaceToUri |
| */ |
| public static String convertUriToNamespace(String uri) |
| { |
| if (uri==null || uri.length()<1) |
| return null; |
| |
| String namespace = uri; |
| |
| int prevIndex = 0; // index of the previously found %, plus 1 |
| int contiguousIndex = 0; // index of next contiguous hex number |
| Byte hexByte = null; |
| ArrayList contiguousBytes = null; |
| String hexChunk = null; |
| |
| for (int i=uri.indexOf(HEX_PREFIX); i>=0; i=uri.indexOf(HEX_PREFIX, prevIndex)) |
| { |
| // get hex byte |
| hexByte = getHexByte(uri, i); |
| contiguousBytes = new ArrayList(); |
| contiguousIndex = i; |
| hexChunk = ""; |
| |
| prevIndex = contiguousIndex+1; // +1 for % |
| |
| // get contiguous bytes |
| while (hexByte!=null) |
| { |
| contiguousBytes.add(hexByte); |
| |
| hexChunk += uri.substring(contiguousIndex, contiguousIndex+3); |
| prevIndex = contiguousIndex+1; // +1 for % |
| |
| hexByte = getHexByte(uri, contiguousIndex+3); // 2 for current hex num, 1 for % |
| |
| contiguousIndex += 3; |
| } |
| prevIndex = prevIndex>=uri.length()?uri.length()-1:prevIndex; |
| |
| if (contiguousBytes.size()<=0) |
| continue; |
| |
| // convert hex string to bytes |
| byte[] cbytes = new byte[contiguousBytes.size()]; |
| for (int j=0; j<cbytes.length; j++) |
| cbytes[j] = ((Byte)contiguousBytes.get(j)).byteValue(); |
| |
| try |
| { |
| // convert bytes to unicode |
| String unicode = new String(cbytes, CHARSET_UTF8); |
| |
| // now find the hex string in the namespace and replace it with the proper unicode |
| int beginIndex = namespace.indexOf(hexChunk); |
| int endIndex = beginIndex+hexChunk.length(); |
| int lastIndex = namespace.length(); |
| endIndex = endIndex>lastIndex?lastIndex:endIndex; |
| |
| namespace = namespace.substring(0, beginIndex) + unicode + namespace.substring(endIndex); |
| } |
| catch(UnsupportedEncodingException e) |
| { |
| // this should never happen -- log anyway |
| // JM: Commented out: |
| //ModelUtilPlugin.logWarning(e, e.getMessage()); |
| } |
| } |
| |
| return namespace; |
| } |
| |
| /** |
| * Returns the Byte value of the hex string at position index in string uri. |
| * |
| * @param uri |
| * @param index |
| * @return null if characters at position index aren't of the form %HH |
| */ |
| protected static Byte getHexByte(String uri, int index) |
| { |
| if (uri==null || uri.length()<1) |
| return null; |
| |
| if (index>=uri.length()) |
| return null; |
| |
| // first character must be % |
| if (!HEX_PREFIX.equals(String.valueOf(uri.charAt(index)))) |
| return null; |
| |
| // hex string is two digits |
| int lastIndex = uri.length(); |
| int endIndex = index+3; |
| |
| // ensure endIndex doesn't extend the length of the string |
| if (endIndex>lastIndex) |
| endIndex = lastIndex; |
| |
| String hexString = uri.substring(index+1, endIndex); |
| byte hexByte = 0; |
| |
| try |
| { |
| hexByte = Integer.valueOf(hexString, 16).byteValue(); |
| } |
| catch (NumberFormatException e) |
| { |
| // not a hex number, return |
| return null; |
| } |
| |
| return Byte.valueOf( hexByte ); |
| } |
| |
| /** |
| * Convert a string intended to be a target namespace, i.e. any string typed with xsd:anyURI. According to the |
| * XSD spec, any string typed with xsd:anyURI cannot contain any characters outside the US-ASCII character set. |
| * These characters must be escaped in the UTF-8 hex notation, %HH. This method also escapes "unwise" characters |
| * by calling escapeUnsafeCharacters first. |
| * |
| * Note that models saving to an XMLResource don't need this method since the XMLResource saves/loads using a |
| * Xerces parser which automatically does this. Models using the SSE parser for saving/loading need this as the |
| * SSE parser doesn't do this automatically. |
| * |
| * @param namespace |
| * @return - UTF-8 character escaped string of any characters outside the US-ASCII character set. |
| * @see convertUriToNamespace |
| * @see escapeUnsafeCharacters |
| */ |
| public static String convertNamespaceToUri(String namespace) |
| { |
| return convertNamespaceToUri(namespace, true); |
| } |
| |
| /** |
| * Convert a string intended to be a target namespace, i.e. any string typed with xsd:anyURI. According to the |
| * XSD spec, any string typed with xsd:anyURI cannot contain any characters outside the US-ASCII character set. |
| * These characters must be escaped in the UTF-8 hex notation, %HH. This method also escapes "unwise" characters |
| * (except for '%' if escapePercent is set to false) by calling escapeUnsafeCharacters first. Setting escapePercent |
| * to false means you can call this method on previously converted strings without the '%' from '%HH' being converted |
| * again. |
| * |
| * Note that models saving to an XMLResource don't need this method since the XMLResource saves/loads using a |
| * Xerces parser which automatically does this. Models using the SSE parser for saving/loading need this as the |
| * SSE parser doesn't do this automatically. |
| * |
| * @param namespace |
| * @param escapePercent - escapes '%' to it's hex value if true, ignores it otherwise. |
| * @return - UTF-8 character escaped string of any characters outside the US-ASCII character set. |
| * @see convertUriToNamespace |
| * @see escapeUnsafeCharacters |
| */ |
| public static String convertNamespaceToUri(String namespace, boolean escapePercent) |
| { |
| if (namespace==null || namespace.length()<1) |
| return null; |
| |
| StringBuffer buffer = new StringBuffer(); |
| |
| // first escape "unsafe" characters |
| namespace = escapeUnsafeCharacters(namespace, escapePercent); |
| |
| char[] nsChars = namespace.toCharArray(); |
| byte[] utf8 = null; |
| |
| try |
| { |
| for (int i=0; i<nsChars.length; i++) |
| { |
| // Convert all characters outside the US-ASCII range (>127) to |
| // UTF-8 bytes as dictated by the anyURI definition in the |
| // XSD spec. |
| if (nsChars[i]>127) |
| { |
| utf8 = String.valueOf(nsChars[i]).getBytes(CHARSET_UTF8); |
| for (int j=0; j<utf8.length; j++) |
| { |
| buffer.append(HEX_PREFIX); |
| |
| // 2-digit hex value |
| buffer.append(Integer.toString(( utf8[j] & 0xff ), 16).toUpperCase()); |
| } |
| } |
| else |
| { |
| buffer.append(nsChars[i]); |
| } |
| } |
| } |
| catch (UnsupportedEncodingException e) |
| { |
| // this should never happen -- log anyway |
| // JM commented out: |
| //ModelUtilPlugin.logWarning(e, e.getMessage()); |
| } |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Escapes the "delim" and "unwise" characters as specified by rfc2396. Also escapes the tilde (~) as this |
| * also seems to cause problems with the XSD validator. The characters are escaped using the UTF-8 hex |
| * notation, %HH. |
| * |
| * To do undo this operation, call convertUriToNamespace |
| * |
| * @param anyURI |
| * @return |
| * @see this is equivalent to escapeUnsafeCharacters(anyURI, true) |
| */ |
| public static String escapeUnsafeCharacters(String anyURI) |
| { |
| return escapeUnsafeCharacters(anyURI, true); |
| } |
| |
| /** |
| * Escapes the "delim" and "unwise" characters as specified by rfc2396. Also escapes the tilde (~) as this |
| * also seems to cause problems with the XSD validator. The characters are escaped using the UTF-8 hex |
| * notation, %HH. |
| * |
| * To do undo this operation, call convertUriToNamespace |
| * |
| * @param anyURI |
| * @param escapePercent - escapes '%' to it's hex value if true, ignores it otherwise. |
| * @return |
| */ |
| public static String escapeUnsafeCharacters(String anyURI, boolean escapePercent) |
| { |
| if (anyURI==null) |
| return null; |
| |
| // must escape % first since our escapes have % in them. |
| if (escapePercent) |
| anyURI = anyURI.replaceAll("\\"+PERCENT, (String)fCharToEscaped.get(PERCENT)); //$NON-NLS-1$ |
| |
| String key = null; |
| |
| // escape all other characters except % |
| for (Iterator i = fCharToEscaped.keySet().iterator(); i.hasNext();) |
| { |
| key = (String)i.next(); |
| if (key.equals(PERCENT)) |
| continue; |
| |
| anyURI = anyURI.replaceAll("\\"+key, (String)fCharToEscaped.get(key)); //$NON-NLS-1$ |
| } |
| |
| return anyURI; |
| } |
| |
| /** |
| * Returns the "delim" and "unwise" characters as specified by rfc2396 if found. Also returns the tilde (~) as this |
| * also seems to cause problems with the XSD validator. Returns null if no unsafe characters found. |
| * |
| * @param anyURI |
| * @param spaceUnsafe - if true, treats a space as an "unwise" character. |
| * @return null if no unsafe characters found, |
| * the unsafe character string otherwise |
| */ |
| public static String containsUnsafeCharacter(String anyURI, boolean spaceUnsafe) |
| { |
| if (anyURI==null) |
| return null; |
| |
| String key = null; |
| |
| for (Iterator i = fCharToEscaped.keySet().iterator(); i.hasNext();) |
| { |
| key = (String)i.next(); |
| |
| if (!spaceUnsafe && SPACE.equals(key)) |
| continue; |
| |
| if (anyURI.indexOf(key)>=0) |
| return key; |
| } |
| |
| // no unsafe characters |
| return null; |
| } |
| } |