| @@ -81,7 +81,7 @@ | |||||
| <td>reverse</td> | <td>reverse</td> | ||||
| <td>Reverse the sense of the conversion, | <td>Reverse the sense of the conversion, | ||||
| i.e. convert from ASCII to native <b>only supported by the | i.e. convert from ASCII to native <b>only supported by the | ||||
| sun converter</b></td> | |||||
| sun and builtin converters</b></td> | |||||
| <td align="center">No</td> | <td align="center">No</td> | ||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| @@ -23,6 +23,7 @@ import java.io.File; | |||||
| import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
| import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
| import java.io.FileReader; | import java.io.FileReader; | ||||
| import java.io.FileWriter; | |||||
| import java.io.IOException; | import java.io.IOException; | ||||
| import java.io.InputStreamReader; | import java.io.InputStreamReader; | ||||
| import java.io.OutputStreamWriter; | import java.io.OutputStreamWriter; | ||||
| @@ -45,20 +46,15 @@ public class BuiltinNative2Ascii implements Native2AsciiAdapter { | |||||
| public final boolean convert(Native2Ascii args, File srcFile, | public final boolean convert(Native2Ascii args, File srcFile, | ||||
| File destFile) throws BuildException { | File destFile) throws BuildException { | ||||
| boolean reverse = args.getReverse(); | |||||
| String encoding = args.getEncoding(); | |||||
| BufferedReader input = null; | BufferedReader input = null; | ||||
| try { | try { | ||||
| if (args.getEncoding() != null) { | |||||
| input = new BufferedReader(new InputStreamReader( | |||||
| new FileInputStream(srcFile), args.getEncoding())); | |||||
| } else { | |||||
| input = new BufferedReader(new FileReader(srcFile)); | |||||
| } | |||||
| input = getReader(srcFile, encoding, reverse); | |||||
| try { | try { | ||||
| BufferedWriter output = new BufferedWriter( | |||||
| new OutputStreamWriter(new FileOutputStream(destFile), | |||||
| "ASCII")); | |||||
| Writer output = getWriter(destFile, encoding, reverse); | |||||
| try { | try { | ||||
| translate(input, output, args.getReverse()); | |||||
| translate(input, output, reverse); | |||||
| } finally { | } finally { | ||||
| FileUtils.close(output); | FileUtils.close(output); | ||||
| } | } | ||||
| @@ -71,11 +67,37 @@ public class BuiltinNative2Ascii implements Native2AsciiAdapter { | |||||
| } | } | ||||
| } | } | ||||
| private BufferedReader getReader(File srcFile, String encoding, | |||||
| boolean reverse) throws IOException { | |||||
| if (!reverse && encoding != null) { | |||||
| return new BufferedReader(new InputStreamReader( | |||||
| new FileInputStream(srcFile), encoding)); | |||||
| } | |||||
| return new BufferedReader(new FileReader(srcFile)); | |||||
| } | |||||
| private Writer getWriter(File destFile, String encoding, | |||||
| boolean reverse) throws IOException { | |||||
| if (!reverse) { | |||||
| encoding = "ASCII"; | |||||
| } | |||||
| if (encoding != null) { | |||||
| return new BufferedWriter( | |||||
| new OutputStreamWriter(new FileOutputStream(destFile), | |||||
| encoding)); | |||||
| } | |||||
| return new BufferedWriter(new FileWriter(destFile)); | |||||
| } | |||||
| private void translate(BufferedReader input, Writer output, | private void translate(BufferedReader input, Writer output, | ||||
| boolean reverse) throws IOException { | boolean reverse) throws IOException { | ||||
| String line = null; | String line = null; | ||||
| while ((line = input.readLine()) != null) { | while ((line = input.readLine()) != null) { | ||||
| output.write(Native2AsciiUtils.native2ascii(line)); | |||||
| if (!reverse) { | |||||
| output.write(Native2AsciiUtils.native2ascii(line)); | |||||
| } else { | |||||
| output.write(Native2AsciiUtils.ascii2native(line)); | |||||
| } | |||||
| output.write(StringUtils.LINE_SEP); | output.write(StringUtils.LINE_SEP); | ||||
| } | } | ||||
| } | } | ||||
| @@ -48,4 +48,42 @@ public class Native2AsciiUtils { | |||||
| } | } | ||||
| return sb.toString(); | return sb.toString(); | ||||
| } | } | ||||
| /** | |||||
| * Replaces Unicode-Escapes. | |||||
| * <p>Expects to be called once per line if applied to a file.</p> | |||||
| * @param line the input line | |||||
| * @return the translated line | |||||
| */ | |||||
| public static String ascii2native(String line) { | |||||
| StringBuilder sb = new StringBuilder(); | |||||
| int inputLen = line.length(); | |||||
| for (int i = 0; i < inputLen; i++) { | |||||
| char c = line.charAt(i); | |||||
| if (c != '\\' || i >= inputLen - 5) { | |||||
| sb.append(c); | |||||
| } else { // backslash with enough remaining characters | |||||
| char u = line.charAt(++i); | |||||
| if (u == 'u') { | |||||
| int unescaped = tryParse(line, i + 1); | |||||
| if (unescaped >= 0) { | |||||
| sb.append((char) unescaped); | |||||
| i += 4; | |||||
| continue; | |||||
| } | |||||
| } | |||||
| // not a unicode escape | |||||
| sb.append(c).append(u); | |||||
| } | |||||
| } | |||||
| return sb.toString(); | |||||
| } | |||||
| private static int tryParse(String line, int startIdx) { | |||||
| try { | |||||
| return Integer.parseInt(line.substring(startIdx, startIdx + 4), 16); | |||||
| } catch (NumberFormatException ex) { | |||||
| return -1; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -95,12 +95,6 @@ public class Adapter implements Native2AsciiAdapter { | |||||
| value="\u00e4\u00f6\u00fc=\u00c4\u00d6\u00dc"/> | value="\u00e4\u00f6\u00fc=\u00c4\u00d6\u00dc"/> | ||||
| </target> | </target> | ||||
| <target name="testUTF8-To-ASCII" depends="-setup-UTF8-To-ASCII"> | |||||
| <native2ascii-def/> | |||||
| <assertTranslatedOutput file="umlauts.properties" encoding="ASCII" | |||||
| expected="${umlauts.expected}"/> | |||||
| </target> | |||||
| <target name="testUTF8-To-ASCII-sun" depends="-setup-UTF8-To-ASCII" | <target name="testUTF8-To-ASCII-sun" depends="-setup-UTF8-To-ASCII" | ||||
| description="https://bz.apache.org/bugzilla/show_bug.cgi?id=59855" | description="https://bz.apache.org/bugzilla/show_bug.cgi?id=59855" | ||||
| unless="jdk1.9+"> | unless="jdk1.9+"> | ||||
| @@ -131,4 +125,11 @@ public class Adapter implements Native2AsciiAdapter { | |||||
| expected="${umlauts.expected}"/> | expected="${umlauts.expected}"/> | ||||
| </target> | </target> | ||||
| <target name="testASCII-To-UTF8-builtin" depends="-setup-ASCII-To-UTF8" | |||||
| description="https://bz.apache.org/bugzilla/show_bug.cgi?id=59855"> | |||||
| <native2ascii-def implementation="builtin" reverse="true"/> | |||||
| <assertTranslatedOutput file="umlauts.properties" encoding="UTF-8" | |||||
| expected="${umlauts.expected}"/> | |||||
| </target> | |||||
| </project> | </project> | ||||
| @@ -43,4 +43,34 @@ public class Native2AsciiUtilsTest { | |||||
| assertEquals("\\u00e4\\u01f6\\u12fc", | assertEquals("\\u00e4\\u01f6\\u12fc", | ||||
| Native2AsciiUtils.native2ascii("\u00e4\u01f6\u12fc")); | Native2AsciiUtils.native2ascii("\u00e4\u01f6\u12fc")); | ||||
| } | } | ||||
| @Test | |||||
| public void doesntTouchNonEscapes() { | |||||
| StringBuilder sb = new StringBuilder(); | |||||
| for (char i = 0; i < 128; i++) { | |||||
| sb.append(i); | |||||
| } | |||||
| assertEquals(sb.toString(), Native2AsciiUtils.ascii2native(sb.toString())); | |||||
| } | |||||
| @Test | |||||
| public void unescapes() { | |||||
| assertEquals("\u00e4\u00f6\u00fc", | |||||
| Native2AsciiUtils.ascii2native("\\u00e4\\u00f6\\u00fc")); | |||||
| } | |||||
| @Test | |||||
| public void leavesNonUnicodeBackslashesAlone() { | |||||
| assertEquals("\\abcdef", Native2AsciiUtils.ascii2native("\\abcdef")); | |||||
| assertEquals("\\u012j", Native2AsciiUtils.ascii2native("\\u012j")); | |||||
| } | |||||
| @Test | |||||
| public void dealsWithUnfinishedEscapes() { | |||||
| assertEquals("\u00e4", Native2AsciiUtils.ascii2native("\\u00e4")); | |||||
| assertEquals("\\u00e", Native2AsciiUtils.ascii2native("\\u00e")); | |||||
| assertEquals("\\u00", Native2AsciiUtils.ascii2native("\\u00")); | |||||
| assertEquals("\\u0", Native2AsciiUtils.ascii2native("\\u0")); | |||||
| } | |||||
| } | } | ||||