diff --git a/WHATSNEW b/WHATSNEW index ab25572ec..3400159ac 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -53,6 +53,11 @@ Other changes: * now supports JDK9 modules Github Pull Request #18 + * a new implementation "builtin" has been added to and + is the default when running on JDK9+ since the tool itself has been + removed from the JDK. + Bugzilla Report 59855 + Changes from Ant 1.9.6 TO Ant 1.9.7 =================================== diff --git a/manual/Tasks/native2ascii.html b/manual/Tasks/native2ascii.html index c50483b9f..ecc065a3d 100644 --- a/manual/Tasks/native2ascii.html +++ b/manual/Tasks/native2ascii.html @@ -61,9 +61,14 @@ with the implementation attribute or a nested element. Here are the choices of the attribute:

@@ -76,7 +81,7 @@ + sun and builtin converters diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/BuiltinNative2Ascii.java b/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/BuiltinNative2Ascii.java new file mode 100644 index 000000000..db91a2638 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/BuiltinNative2Ascii.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.taskdefs.optional.native2ascii; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.optional.Native2Ascii; +import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.Native2AsciiUtils; +import org.apache.tools.ant.util.StringUtils; + +/** + * Encapsulates the built-in Native2Ascii implementation. + * + * @since Ant 1.9.8 + */ +public class BuiltinNative2Ascii implements Native2AsciiAdapter { + + static final String IMPLEMENTATION_NAME = "builtin"; + + public final boolean convert(Native2Ascii args, File srcFile, + File destFile) throws BuildException { + boolean reverse = args.getReverse(); + String encoding = args.getEncoding(); + BufferedReader input = null; + try { + input = getReader(srcFile, encoding, reverse); + try { + Writer output = getWriter(destFile, encoding, reverse); + try { + translate(input, output, reverse); + } finally { + FileUtils.close(output); + } + } finally { + FileUtils.close(input); + } + return true; + } catch (IOException ex) { + throw new BuildException("Exception trying to translate data", ex); + } + } + + 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, + boolean reverse) throws IOException { + String line = null; + while ((line = input.readLine()) != null) { + if (!reverse) { + output.write(Native2AsciiUtils.native2ascii(line)); + } else { + output.write(Native2AsciiUtils.ascii2native(line)); + } + output.write(StringUtils.LINE_SEP); + } + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java b/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java index ae4190395..e8bd74d16 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java @@ -40,10 +40,13 @@ public class Native2AsciiAdapterFactory { * vendor */ public static String getDefault() { - if (JavaEnvUtils.isKaffe() || JavaEnvUtils.isClasspathBased()) { + if (shouldUseKaffee()) { return KaffeNative2Ascii.IMPLEMENTATION_NAME; } - return SunNative2Ascii.IMPLEMENTATION_NAME; + if (shouldUseSun()) { + return SunNative2Ascii.IMPLEMENTATION_NAME; + } + return BuiltinNative2Ascii.IMPLEMENTATION_NAME; } /** @@ -79,11 +82,14 @@ public class Native2AsciiAdapterFactory { ProjectComponent log, Path classpath) throws BuildException { - if (((JavaEnvUtils.isKaffe() || JavaEnvUtils.isClasspathBased()) && choice == null) + if ((shouldUseKaffee() && choice == null) || KaffeNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) { return new KaffeNative2Ascii(); - } else if (SunNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) { + } else if ((shouldUseSun() && choice == null) + || SunNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) { return new SunNative2Ascii(); + } else if (BuiltinNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) { + return new BuiltinNative2Ascii(); } else if (choice != null) { return resolveClassName(choice, // Memory leak in line below @@ -91,9 +97,7 @@ public class Native2AsciiAdapterFactory { .createClassLoader(classpath)); } - // This default has been good enough until Ant 1.6.3, so stick - // with it - return new SunNative2Ascii(); + return new BuiltinNative2Ascii(); } /** @@ -113,4 +117,15 @@ public class Native2AsciiAdapterFactory { Native2AsciiAdapterFactory.class.getClassLoader(), Native2AsciiAdapter.class); } + + private static final boolean shouldUseKaffee() { + return JavaEnvUtils.isKaffe() || JavaEnvUtils.isClasspathBased(); + } + + private static final boolean shouldUseSun() { + return JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5) + || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_6) + || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_7) + || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_8); + } } diff --git a/src/main/org/apache/tools/ant/util/Native2AsciiUtils.java b/src/main/org/apache/tools/ant/util/Native2AsciiUtils.java new file mode 100644 index 000000000..a69d0313d --- /dev/null +++ b/src/main/org/apache/tools/ant/util/Native2AsciiUtils.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.util; + +/** + * Contains helper methods for Ant's built-in implementation of native2ascii. + * + * @since Ant 1.9.8 + */ +public class Native2AsciiUtils { + + private static final int MAX_ASCII = 127; + + /** + * Replaces non-ASCII characters with their Unicode-Escapes. + *

Expects to be called once per line if applied to a file.

+ * @param line the input line + * @return the translated line + */ + public static String native2ascii(String line) { + StringBuilder sb = new StringBuilder(); + for (char c : line.toCharArray()) { + if (c <= MAX_ASCII) { + sb.append(c); + } else { + sb.append("\\u"); + String encoded = Integer.toHexString(c); + for (int i = encoded.length(); i < 4; i++) { + sb.append("0"); + } + sb.append(encoded); + } + } + return sb.toString(); + } + + /** + * Replaces Unicode-Escapes. + *

Expects to be called once per line if applied to a file.

+ * @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; + } + } +} diff --git a/src/tests/antunit/taskdefs/optional/native2ascii-test.xml b/src/tests/antunit/taskdefs/optional/native2ascii-test.xml index 7a30df795..be521de7f 100644 --- a/src/tests/antunit/taskdefs/optional/native2ascii-test.xml +++ b/src/tests/antunit/taskdefs/optional/native2ascii-test.xml @@ -65,4 +65,71 @@ public class Adapter implements Native2AsciiAdapter { + + + + + + + + + + + + + + + + + + + + + + + + äöü=ÄÖÜ + + + + + + + + + + + + + + + + + \u00e4\u00f6\u00fc=\u00c4\u00d6\u00dc + + + + + + + + + + + + + diff --git a/src/tests/junit/org/apache/tools/ant/util/Native2AsciiUtilsTest.java b/src/tests/junit/org/apache/tools/ant/util/Native2AsciiUtilsTest.java new file mode 100644 index 000000000..f3b54e619 --- /dev/null +++ b/src/tests/junit/org/apache/tools/ant/util/Native2AsciiUtilsTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.util; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class Native2AsciiUtilsTest { + + @Test + public void doesntTouchAscii() { + StringBuilder sb = new StringBuilder(); + for (char i = 0; i < 128; i++) { + sb.append(i); + } + assertEquals(sb.toString(), Native2AsciiUtils.native2ascii(sb.toString())); + } + + @Test + public void escapes() { + assertEquals("\\u00e4\\u00f6\\u00fc", + Native2AsciiUtils.native2ascii("\u00e4\u00f6\u00fc")); + } + + @Test + public void pads() { + assertEquals("\\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")); + } + +}
reverse Reverse the sense of the conversion, i.e. convert from ASCII to native only supported by the - sun converter No