blob: 484b2247fa6d54068cf47486957ae09f48829816 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 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.jdt.internal.ui.propertiesfileeditor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.ui.dialogs.StatusInfo;
/**
* Helper class to convert between Java chars and the escaped form that must be used in .properties
* files.
*
* @since 3.7
*/
public class PropertiesFileEscapes {
private static final char[] HEX_DIGITS= { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
private static char toHex(int halfByte) {
return HEX_DIGITS[(halfByte & 0xF)];
}
/**
* Returns the decimal value of the Hex digit, or -1 if the digit is not a valid Hex digit.
*
* @param digit the Hex digit
* @return the decimal value of digit, or -1 if digit is not a valid Hex digit.
*/
private static int getHexDigitValue(char digit) {
switch (digit) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return digit - '0';
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
return 10 + digit - 'a';
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
return 10 + digit - 'A';
default:
return -1;
}
}
/**
* Convert a Java char to the escaped form that must be used in .properties files.
*
* @param c the Java char
* @return escaped string
*/
public static String escape(char c) {
return escape(c, true, true, true);
}
/**
* Convert characters in a Java string to the escaped form that must be used in .properties
* files.
*
* @param s the Java string
* @param escapeWhitespaceChars if <code>true</code>, escape whitespace characters
* @param escapeBackslash if <code>true</code>, escape backslash characters
* @param escapeUnicodeChars if <code>true</code>, escape unicode characters
* @return escaped string
*/
public static String escape(String s, boolean escapeWhitespaceChars, boolean escapeBackslash, boolean escapeUnicodeChars) {
StringBuffer sb= new StringBuffer(s.length());
int length= s.length();
for (int i= 0; i < length; i++) {
char c= s.charAt(i);
sb.append(escape(c, escapeWhitespaceChars, escapeBackslash, escapeUnicodeChars));
}
return sb.toString();
}
/**
* Convert a Java char to the escaped form that must be used in .properties files.
*
* @param c the Java char
* @param escapeWhitespaceChars if <code>true</code>, escape whitespace characters
* @param escapeBackslash if <code>true</code>, escape backslash characters
* @param escapeUnicodeChars if <code>true</code>, escape unicode characters
* @return escaped string
*/
public static String escape(char c, boolean escapeWhitespaceChars, boolean escapeBackslash, boolean escapeUnicodeChars) {
switch (c) {
case '\t':
return escapeWhitespaceChars ? "\\t" : "\t"; //$NON-NLS-1$//$NON-NLS-2$
case '\n':
return escapeWhitespaceChars ? "\\n" : "\n"; //$NON-NLS-1$//$NON-NLS-2$
case '\f':
return escapeWhitespaceChars ? "\\f" : "\r"; //$NON-NLS-1$//$NON-NLS-2$
case '\r':
return escapeWhitespaceChars ? "\\r" : "\r"; //$NON-NLS-1$//$NON-NLS-2$
case '\\':
return escapeBackslash ? "\\\\" : "\\"; //$NON-NLS-1$ //$NON-NLS-2$
default:
if (escapeUnicodeChars && ((c < 0x0020) || (c > 0x007e && c <= 0x00a0) || (c > 0x00ff))) {
//NBSP (0x00a0) is escaped to differentiate from normal space character
return new StringBuffer()
.append('\\')
.append('u')
.append(toHex((c >> 12) & 0xF))
.append(toHex((c >> 8) & 0xF))
.append(toHex((c >> 4) & 0xF))
.append(toHex(c & 0xF)).toString();
} else
return String.valueOf(c);
}
}
/**
* Convert an escaped string to a string composed of Java characters.
*
* @param s the escaped string
* @return string composed of Java characters
* @throws CoreException if the escaped string has a malformed \\uxxx sequence
*/
public static String unescape(String s) throws CoreException {
boolean isValidEscapedString= true;
if (s == null)
return null;
char aChar;
int len= s.length();
StringBuffer outBuffer= new StringBuffer(len);
for (int x= 0; x < len;) {
aChar= s.charAt(x++);
if (aChar == '\\') {
if (x > len - 1) {
return outBuffer.toString(); // silently ignore the \
}
aChar= s.charAt(x++);
if (aChar == 'u') {
// Read the xxxx
int value= 0;
if (x > len - 4) {
String exceptionMessage= Messages.format(PropertiesFileEditorMessages.PropertiesFileHover_MalformedEncoding, outBuffer.toString() + s.substring(x - 2));
throw new CoreException(new StatusInfo(IStatus.WARNING, exceptionMessage));
}
StringBuffer buf= new StringBuffer("\\u"); //$NON-NLS-1$
int digit= 0;
for (int i= 0; i < 4; i++) {
aChar= s.charAt(x++);
digit= getHexDigitValue(aChar);
if (digit == -1) {
isValidEscapedString= false;
x--;
break;
}
value= (value << 4) + digit;
buf.append(aChar);
}
outBuffer.append(digit == -1 ? buf.toString() : String.valueOf((char)value));
} else if (aChar == 't') {
outBuffer.append('\t');
} else if (aChar == 'n') {
outBuffer.append('\n');
} else if (aChar == 'f') {
outBuffer.append('\f');
} else if (aChar == 'r') {
outBuffer.append('\r');
} else {
outBuffer.append(aChar); // silently ignore the \
}
} else
outBuffer.append(aChar);
}
if (isValidEscapedString) {
return outBuffer.toString();
} else {
String exceptionMessage= Messages.format(PropertiesFileEditorMessages.PropertiesFileHover_MalformedEncoding, outBuffer.toString());
throw new CoreException(new StatusInfo(IStatus.WARNING, exceptionMessage));
}
}
/**
* Unescape backslash characters in a string.
*
* @param s the escaped string
* @return string with backslash characters unescaped
*/
public static String unescapeBackslashes(String s) {
if (s == null)
return null;
char c;
int length= s.length();
StringBuffer outBuffer= new StringBuffer(length);
for (int i= 0; i < length;) {
c= s.charAt(i++);
if (c == '\\') {
c= s.charAt(i++);
}
outBuffer.append(c);
}
return outBuffer.toString();
}
/**
* Tests if the given text contains any invalid escape sequence.
*
* @param text the text
* @return <code>true</code> if text contains an invalid escape sequence, <code>false</code>
* otherwise
*/
public static boolean containsInvalidEscapeSequence(String text) {
try {
//check for invalid unicode escapes
unescape(text);
} catch (CoreException e) {
return true;
}
int length= text.length();
for (int i= 0; i < length; i++) {
char c= text.charAt(i);
if (c == '\\') {
if (i < length - 1) {
char nextC= text.charAt(i + 1);
switch (nextC) {
case 't':
case 'n':
case 'f':
case 'r':
case 'u':
case '\n':
case '\r':
case '=':
case ':':
break;
case '\\':
i++;
break;
default:
return true;
}
} else {
return true;
}
}
}
return false;
}
/**
* Tests if the given text contains an unescaped backslash character.
*
* @param text the text
* @return <code>true</code> if text contains an unescaped backslash character,
* <code>false</code> otherwise
*/
public static boolean containsUnescapedBackslash(String text) {
int length= text.length();
for (int i= 0; i < length; i++) {
char c= text.charAt(i);
if (c == '\\') {
if (i < length - 1) {
char nextC= text.charAt(i + 1);
switch (nextC) {
case '\\':
i++;
break;
default:
return true;
}
} else {
return true;
}
}
}
return false;
}
/**
* Tests if the given text contains only escaped backslash characters and no unescaped backslash
* character.
*
* @param text the text
* @return <code>true</code> if text contains only escaped backslash characters,
* <code>false</code> otherwise
*/
public static boolean containsEscapedBackslashes(String text) {
boolean result= false;
int length= text.length();
for (int i= 0; i < length; i++) {
char c= text.charAt(i);
if (c == '\\') {
if (i < length - 1) {
char nextC= text.charAt(i + 1);
switch (nextC) {
case '\\':
i++;
result= true;
break;
default:
return false;
}
} else {
return false;
}
}
}
return result;
}
}