| /******************************************************************************* |
| * 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; |
| } |
| } |