/******************************************************************************* | |
* Copyright (c) 2005, 2012 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* 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( char nsChar : nsChars ) { | |
// Convert all characters outside the US-ASCII range (>127) to | |
// UTF-8 bytes as dictated by the anyURI definition in the | |
// XSD spec. | |
if (nsChar>127) | |
{ | |
utf8 = String.valueOf(nsChar).getBytes(CHARSET_UTF8); | |
for( byte element : utf8 ) { | |
buffer.append(HEX_PREFIX); | |
// 2-digit hex value | |
buffer.append(Integer.toString(( element & 0xff ), 16).toUpperCase()); | |
} | |
} | |
else | |
{ | |
buffer.append(nsChar); | |
} | |
} | |
} | |
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 | |
* @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; | |
} | |
} |