diff --git a/CONTRIBUTORS b/CONTRIBUTORS index af4f861d0..e8ed2daf4 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -73,6 +73,7 @@ David Gärtner David S. Johnson David Kavanagh David LeRoy +David Leal David M. Lloyd David Maclean David Rees diff --git a/WHATSNEW b/WHATSNEW index efaec479d..387733d09 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -923,6 +923,10 @@ Other changes: parent directory of the destination archive if it doesn't exist. Bugzilla Report 45377. + * A new filterreader that sorts input lines has been + added. + Bugzilla Report 40504. + Changes from Ant 1.7.0 TO Ant 1.7.1 ============================================= diff --git a/contributors.xml b/contributors.xml index 8787c9109..a965c5ad7 100644 --- a/contributors.xml +++ b/contributors.xml @@ -316,6 +316,10 @@ David LeRoy + + David + Leal + David M. diff --git a/docs/manual/CoreTypes/filterchain.html b/docs/manual/CoreTypes/filterchain.html index b63e18d67..6e2ef1e0b 100644 --- a/docs/manual/CoreTypes/filterchain.html +++ b/docs/manual/CoreTypes/filterchain.html @@ -124,6 +124,7 @@ nested elements.
ConcatFilter
TokenFilter
FixCRLF
+SortFilter

FilterReader

@@ -1481,7 +1482,7 @@ public class Capitalize This may be used as follows:
-  <typedef type="capitalize" classname="my.customant.Capitalize"
+  <typedef name="capitalize" classname="my.customant.Capitalize"
            classpath="my.customant.path"/>
   <copy file="input" tofile="output">
     <filterchain>
@@ -1493,4 +1494,97 @@ This may be used as follows:
   </copy>
 
+

SortFilter

+ +

This filter sorts lines lexicographically but you can specifiy a + custom comparator as well.

+ + + + + + + + + + + + + + + + + +
Parameter TypeParameter ValueRequired
reverseWhether to reverse the sort order + (boolean). Will be ignored if a custom comparator has been + specified.No
comparatorClassname of a class + implementing java.util.Comparator an instance of + which will be used when comparing lines.No
+

+

Example:

+ +This will sort the lines. +
+<filterreader classname="org.apache.tools.ant.filters.SortFilter"/>
+
+ +Convenience method: +
+<sortfilter/>
+
+ +This will reverse the sort order. + +
+<filterreader classname="org.apache.tools.ant.filters.SortFilter">
+  <param name="reverse" value="true"/>
+</filterreader>
+
+ +Convenience method: +
+<sortfilter reverse="true"/>
+
+ +You can use your own comparator, the easiest way is by using typedef +and the convenience method which allows to specify the comparator as a +nested element: +
+public final class EvenFirstCmp implements Comparator {
+
+    public int compare(Object o1, Object o2) {
+        String s1 = ((String) o1).substring(5).trim();
+        String s2 = ((String) o2).substring(5).trim();
+        int n1 = Integer.parseInt(s1);
+        int n2 = Integer.parseInt(s2);
+        if (n1 % 2 == 0) {
+            if (n2 % 2 == 0) {
+                return n1 - n2;
+            } else {
+                return -1;
+            }
+        } else {
+            if (n2 % 2 == 0) {
+                return 1;
+            } else {
+                return n1 - n2;
+            }
+        }
+    }
+}
+
+ +and used as + +
+<typedef classname="org.apache.tools.ant.filters.EvenFirstCmp"
+         name="evenfirst"/>
+...
+  <filterchain>
+    <sortfilter>
+      <evenfirst/>
+    </sortfilter>
+  </filterchain>
+
+ diff --git a/src/main/org/apache/tools/ant/antlib.xml b/src/main/org/apache/tools/ant/antlib.xml index d274d09f4..a3cdac4d9 100644 --- a/src/main/org/apache/tools/ant/antlib.xml +++ b/src/main/org/apache/tools/ant/antlib.xml @@ -51,6 +51,8 @@ classname="org.apache.tools.ant.taskdefs.condition.IsFailure"/> + + + + diff --git a/src/main/org/apache/tools/ant/filters/SortFilter.java b/src/main/org/apache/tools/ant/filters/SortFilter.java new file mode 100644 index 000000000..31d9c39ec --- /dev/null +++ b/src/main/org/apache/tools/ant/filters/SortFilter.java @@ -0,0 +1,362 @@ +/* + * 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.filters; + +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Parameter; + +/** + *

+ * Sort a file before and/or after the file. + *

+ * + *

+ * Examples: + *

+ * + *
+ *   <copy todir="build">
+ *       <fileset dir="input" includes="*.txt"/>
+ *       <filterchain>
+ *           <sortfilter/>
+ *       </filterchain>
+ *   </copy>
+ * 
+ * + *

+ * Sort all files *.txt from src location in descendant + * order and copy them into build location. The lines of each file are + * sorted in ascendant order comparing the lines vía + * String.compareTo(Object o) method. + *

+ * + *
+ *   <copy todir="build">
+ *       <fileset dir="input" includes="*.txt"/>
+ *       <filterchain>
+ *           <sortfilter/>
+ *             <param name="reverse" value="true"/>
+ *           </sortfilter/>
+ *       </filterchain>
+ *   </copy>
+ * 
+ * + *

+ * Sort all files *.txt from src location into reverse + * order and copy them into build location. If reverse parameter has + * value true (default value), then the output line of the files + * will be in ascendant order. + *

+ * + *
+ *   <copy todir="build">
+ *       <fileset dir="input" includes="*.txt"/>
+ *       <filterchain>
+ *           <sortfilter/>
+ *             <param name="comparator" value="org.apache.tools.ant.filters.EvenFirstCmp"/>
+ *           </sortfilter/>
+ *       </filterchain>
+ *   </copy>
+ * 
+ * + *

+ * Sort all files *.txt from src location using as + * sorting criterium EvenFirstCmp class, that sorts the file + * lines putting even lines first then odd lines for example. The modified files + * are copied into build location. The EventFirstCmp, + * has to an instanciable class via Class.newInstance(), + * therefore in case of inner class has to be static. It also has to + * implement java.util.Comparator interface, for example: + *

+ * + *
+ *         package org.apache.tools.ant.filters;
+ *         ...(omitted)
+ *           public final class EvenFirstCmp implements <b>Comparator</b> {
+ *             public int compare(Object o1, Object o2) {
+ *             ...(omitted)
+ *             }
+ *           }
+ * 
+ * + *

If parameter comparator is present, then + * reverse parameter will not taken into account.

+ * + * @since Ant 1.8.0 + */ +public final class SortFilter extends BaseParamFilterReader + implements ChainableReader { + + /** Parameter name for reverse order. */ + private static final String REVERSE_KEY = "reverse"; + + /** + * Parameter name for specifying the comparator criteria via class that + * implement java.util.Comparator interface. + */ + private static final String COMPARATOR_KEY = "comparator"; + + /** + * Instance of comparator class to be used for sorting. + */ + private Comparator comparator = null; + + /** + * Controls if the sorting process will be in ascendant/descendant order. If + * If has value true, then the line of the file will be + * sorted on descendant order. Default value: false. It will + * be considered only if comparator is null. + */ + private boolean reverse; + + /** + * Stores the lines to be sorted. + */ + private List lines; + + /** + * Remaining line to be read from this filter, or null if the + * next call to read() should read the original stream to + * find the next matching line. + */ + private String line = null; + + private Iterator iterator = null; + + /** + * Constructor for "dummy" instances. + * + * @see BaseFilterReader#BaseFilterReader() + */ + public SortFilter() { + super(); + } + + /** + * Creates a new filtered reader. + * + * @param in + * A Reader object providing the underlying stream. Must not be + * null. + */ + public SortFilter(final Reader in) { + super(in); + } + + /** + * Returns the next character in the filtered stream. If the desired number + * of lines have already been read, the resulting stream is effectively at + * an end. Otherwise, the next character from the underlying stream is read + * and returned. + * + * @return the next character in the resulting stream, or -1 if the end of + * the resulting stream has been reached + * + * @exception IOException + * if the underlying stream throws an IOException during + * reading + */ + public int read() throws IOException { + if (!getInitialized()) { + initialize(); + setInitialized(true); + } + + int ch = -1; + if (line != null) { + /* + * We are on the state: "reading the current line", lines are + * already sorted + */ + ch = line.charAt(0); + if (line.length() == 1) { + line = null; + } else { + line = line.substring(1); + } + } else { + if (lines == null) { + // We read all lines and sort them + lines = new ArrayList(); + for (line = readLine(); line != null; line = readLine()) { + lines.add(line); + } + sort(); + iterator = lines.iterator(); + } + + if (iterator.hasNext()) { + line = (String) iterator.next(); + } else { + line = null; + lines = null; + iterator = null; + } + if (line != null) { + return read(); + } + } + return ch; + } + + /** + * Creates a new SortReader using the passed in Reader for instantiation. + * + * @param rdr + * A Reader object providing the underlying stream. Must not be + * null. + * + * @return a new filter based on this configuration, but filtering the + * specified reader + */ + public Reader chain(final Reader rdr) { + SortFilter newFilter = new SortFilter(rdr); + newFilter.setReverse(isReverse()); + newFilter.setComparator(getComparator()); + newFilter.setInitialized(true); + return newFilter; + } + + /** + * Returns true if the sorting process will be in reverse + * order, otherwise the sorting process will be in ascendant order. + * + * @return true if the sorting process will be in reverse + * order, otherwise the sorting process will be in ascendant order. + */ + public boolean isReverse() { + return reverse; + } + + /** + * Sets the sorting process will be in ascendant (reverse=false) + * or to descendant (reverse=true). + * + * @param reverse + * Boolean representing reverse ordering process. + */ + public void setReverse(boolean reverse) { + this.reverse = reverse; + } + + /** + * Returns the comparator to be used for sorting. + * + * @return the comparator + */ + public Comparator getComparator() { + return comparator; + } + + /** + * Set the comparator to be used as sorting criterium. + * + * @param comparator + * the comparator to set + */ + public void setComparator(Comparator comparator) { + this.comparator = comparator; + } + + /** + * Set the comparator to be used as sorting criterium as nested element. + * + * @param comparator + * the comparator to set + */ + public void add(Comparator comparator) { + if (this.comparator != null && comparator != null) { + throw new BuildException("can't have more than one comparator"); + } + setComparator(comparator); + } + + /** + * Scans the parameters list + */ + private void initialize() throws IOException { + // get parameters + Parameter[] params = getParameters(); + if (params != null) { + for (int i = 0; i < params.length; i++) { + final String paramName = params[i].getName(); + if (REVERSE_KEY.equals(paramName)) { + setReverse(Boolean.valueOf(params[i].getValue()) + .booleanValue()); + continue; + } + if (COMPARATOR_KEY.equals(paramName)) { + try { + String className = (String) params[i].getValue(); + setComparator((Comparator) (Class.forName(className) + .newInstance())); + continue; + } catch (InstantiationException e) { + throw new BuildException(e); + } catch (IllegalAccessException e) { + /* + * Probably a inner non-static class, this this case is + * not considered + */ + throw new BuildException(e); + } catch (ClassNotFoundException e) { + throw new BuildException(e); + } catch (ClassCastException e) { + throw new BuildException("Value of comparator attribute" + + " should implement" + + " java.util.Comparator" + + " interface"); + } catch (Exception e) { + throw new BuildException(e); + } + } + } + } + } + + /** + * Sorts the read lines (lines)acording to the sorting + * criteria defined by the user. + * + */ + private void sort() { + if (comparator == null) { + if (reverse) { + Collections.sort(lines, new Comparator() { + public int compare(Object o1, Object o2) { + String s1 = (String) o1; + String s2 = (String) o2; + return (-s1.compareTo(s2)); + } + }); + } else { + Collections.sort(lines); + } + } else { + Collections.sort(lines, comparator); + } + } +} diff --git a/src/tests/antunit/filters/expected/sort.sortComparator.test b/src/tests/antunit/filters/expected/sort.sortComparator.test new file mode 100644 index 000000000..a93e4f489 --- /dev/null +++ b/src/tests/antunit/filters/expected/sort.sortComparator.test @@ -0,0 +1,10 @@ +Line 2 +Line 4 +Line 6 +Line 8 +Line 10 +Line 1 +Line 3 +Line 5 +Line 7 +Line 9 diff --git a/src/tests/antunit/filters/expected/sort.sortDefault.test b/src/tests/antunit/filters/expected/sort.sortDefault.test new file mode 100644 index 000000000..43d44e572 --- /dev/null +++ b/src/tests/antunit/filters/expected/sort.sortDefault.test @@ -0,0 +1,10 @@ +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 +Line 6 +Line 7 +Line 8 +Line 9 +Line 10 diff --git a/src/tests/antunit/filters/expected/sort.sortReverse.test b/src/tests/antunit/filters/expected/sort.sortReverse.test new file mode 100644 index 000000000..a847cc10e --- /dev/null +++ b/src/tests/antunit/filters/expected/sort.sortReverse.test @@ -0,0 +1,10 @@ +Line 10 +Line 9 +Line 8 +Line 7 +Line 6 +Line 5 +Line 4 +Line 3 +Line 2 +Line 1 diff --git a/src/tests/antunit/filters/input/sort.sortDefault.test b/src/tests/antunit/filters/input/sort.sortDefault.test new file mode 100644 index 000000000..05661185e --- /dev/null +++ b/src/tests/antunit/filters/input/sort.sortDefault.test @@ -0,0 +1,10 @@ +Line 10 +Line 1 +Line 9 +Line 6 +Line 3 +Line 2 +Line 4 +Line 5 +Line 7 +Line 8 diff --git a/src/tests/antunit/filters/sort-test.xml b/src/tests/antunit/filters/sort-test.xml new file mode 100644 index 000000000..8c2a88b51 --- /dev/null +++ b/src/tests/antunit/filters/sort-test.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +