Browse Source

new filter sortfilter by David Leal. PR 40504

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@808009 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 16 years ago
parent
commit
927fb18631
11 changed files with 639 additions and 1 deletions
  1. +1
    -0
      CONTRIBUTORS
  2. +4
    -0
      WHATSNEW
  3. +4
    -0
      contributors.xml
  4. +95
    -1
      docs/manual/CoreTypes/filterchain.html
  5. +7
    -0
      src/main/org/apache/tools/ant/antlib.xml
  6. +362
    -0
      src/main/org/apache/tools/ant/filters/SortFilter.java
  7. +10
    -0
      src/tests/antunit/filters/expected/sort.sortComparator.test
  8. +10
    -0
      src/tests/antunit/filters/expected/sort.sortDefault.test
  9. +10
    -0
      src/tests/antunit/filters/expected/sort.sortReverse.test
  10. +10
    -0
      src/tests/antunit/filters/input/sort.sortDefault.test
  11. +126
    -0
      src/tests/antunit/filters/sort-test.xml

+ 1
- 0
CONTRIBUTORS View File

@@ -73,6 +73,7 @@ David Gärtner
David S. Johnson
David Kavanagh
David LeRoy
David Leal
David M. Lloyd
David Maclean
David Rees


+ 4
- 0
WHATSNEW View File

@@ -923,6 +923,10 @@ Other changes:
parent directory of the destination archive if it doesn't exist.
Bugzilla Report 45377.

* A new filterreader <sortfilter> that sorts input lines has been
added.
Bugzilla Report 40504.

Changes from Ant 1.7.0 TO Ant 1.7.1
=============================================



+ 4
- 0
contributors.xml View File

@@ -316,6 +316,10 @@
<first>David</first>
<last>LeRoy</last>
</name>
<name>
<first>David</first>
<last>Leal</last>
</name>
<name>
<first>David</first>
<middle>M.</middle>


+ 95
- 1
docs/manual/CoreTypes/filterchain.html View File

@@ -124,6 +124,7 @@ nested elements.<br>
<a href="#concatfilter">ConcatFilter</a><br>
<a href="#tokenfilter">TokenFilter</a><br>
<a href="../CoreTasks/fixcrlf.html">FixCRLF</a><br>
<a href="#sortfilter">SortFilter</a><br>

<h3><a name="filterreader">FilterReader</a></h3>

@@ -1481,7 +1482,7 @@ public class Capitalize

This may be used as follows:
<blockquote><pre>
&lt;typedef type="capitalize" classname="my.customant.Capitalize"
&lt;typedef name="capitalize" classname="my.customant.Capitalize"
classpath="my.customant.path"/&gt;
&lt;copy file="input" tofile="output"&gt;
&lt;filterchain&gt;
@@ -1493,4 +1494,97 @@ This may be used as follows:
&lt;/copy&gt;
</pre></blockquote>

<h3><a name="sortfilter">SortFilter</a></h3>

<p>This filter sorts lines lexicographically but you can specifiy a
custom comparator as well.</p>

<table cellSpacing=0 cellPadding=2 border=1>
<tr>
<td vAlign=top><b>Parameter Type</b></td>
<td vAlign=top><b>Parameter Value</b></td>
<td vAlign=top align="center"><b>Required</b></td>
</tr>
<tr>
<td vAlign=top>reverse</td>
<td vAlign=top align="center">Whether to reverse the sort order
(boolean). Will be ignored if a custom comparator has been
specified.</td>
<td vAlign=top align="center">No</td>
</tr>
<tr>
<td vAlign=top>comparator</td>
<td vAlign=top align="center">Classname of a class
implementing <code>java.util.Comparator</code> an instance of
which will be used when comparing lines.</td>
<td vAlign=top align="center">No</td>
</tr>
</table>
<p>
<h4>Example:</h4>

This will sort the lines.
<blockquote><pre>
&lt;filterreader classname=&quot;org.apache.tools.ant.filters.SortFilter&quot;/&gt;
</pre></blockquote>

Convenience method:
<blockquote><pre>
&lt;sortfilter/&gt;
</pre></blockquote>

This will reverse the sort order.

<blockquote><pre>
&lt;filterreader classname=&quot;org.apache.tools.ant.filters.SortFilter&quot;&gt;
&lt;param name=&quot;reverse&quot; value=&quot;true&quot;/&gt;
&lt;/filterreader&gt;
</pre></blockquote>

Convenience method:
<blockquote><pre>
&lt;sortfilter reverse="true"/&gt;
</pre></blockquote>

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:
<blockquote><pre>
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;
}
}
}
}
</pre></blockquote>

and used as

<blockquote><pre>
&lt;typedef classname="org.apache.tools.ant.filters.EvenFirstCmp"
name="evenfirst"/>
...
&lt;filterchain>
&lt;sortfilter>
&lt;evenfirst/>
&lt;/sortfilter>
&lt;/filterchain>
</pre></blockquote>

</body></html>

+ 7
- 0
src/main/org/apache/tools/ant/antlib.xml View File

@@ -51,6 +51,8 @@
classname="org.apache.tools.ant.taskdefs.condition.IsFailure"/>
<componentdef name="isfalse" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.IsFalse"/>
<componentdef name="islastmodified" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.IsLastModified"/>
<componentdef name="isreachable" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.IsReachable"/>
<componentdef name="isreference" onerror="ignore"
@@ -73,6 +75,8 @@
classname="org.apache.tools.ant.taskdefs.condition.ParserSupports"/>
<componentdef name="resourcecontains" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.ResourceContains"/>
<componentdef name="resourceexists" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.ResourceExists"/>
<componentdef name="resourcesmatch" onerror="ignore"
classname="org.apache.tools.ant.taskdefs.condition.ResourcesMatch"/>
<componentdef name="socket" onerror="ignore"
@@ -131,5 +135,8 @@
<componentdef name="reverse" onerror="ignore"
classname="org.apache.tools.ant.types.resources.comparators.Reverse" />

<!-- filters -->
<componentdef name="sortfilter" onerror="ignore"
classname="org.apache.tools.ant.filters.SortFilter"/>
</antlib>


+ 362
- 0
src/main/org/apache/tools/ant/filters/SortFilter.java View File

@@ -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;

/**
* <p>
* Sort a file before and/or after the file.
* </p>
*
* <p>
* Examples:
* </p>
*
* <pre>
* &lt;copy todir=&quot;build&quot;&gt;
* &lt;fileset dir=&quot;input&quot; includes=&quot;*.txt&quot;/&gt;
* &lt;filterchain&gt;
* &lt;sortfilter/&gt;
* &lt;/filterchain&gt;
* &lt;/copy&gt;
* </pre>
*
* <p>
* Sort all files <code>*.txt</code> from <i>src</i> location in descendant
* order and copy them into <i>build</i> location. The lines of each file are
* sorted in ascendant order comparing the lines vía
* <code>String.compareTo(Object o)</code> method.
* </p>
*
* <pre>
* &lt;copy todir=&quot;build&quot;&gt;
* &lt;fileset dir=&quot;input&quot; includes=&quot;*.txt&quot;/&gt;
* &lt;filterchain&gt;
* &lt;sortfilter/&gt;
* &lt;param name=&quot;reverse&quot; value=&quot;true&quot;/&gt;
* &lt;/sortfilter/&gt;
* &lt;/filterchain&gt;
* &lt;/copy&gt;
* </pre>
*
* <p>
* Sort all files <code>*.txt</code> from <i>src</i> location into reverse
* order and copy them into <i>build</i> location. If reverse parameter has
* value <code>true</code> (default value), then the output line of the files
* will be in ascendant order.
* </p>
*
* <pre>
* &lt;copy todir=&quot;build&quot;&gt;
* &lt;fileset dir=&quot;input&quot; includes=&quot;*.txt&quot;/&gt;
* &lt;filterchain&gt;
* &lt;sortfilter/&gt;
* &lt;param name=&quot;comparator&quot; value=&quot;org.apache.tools.ant.filters.EvenFirstCmp&quot;/&gt;
* &lt;/sortfilter/&gt;
* &lt;/filterchain&gt;
* &lt;/copy&gt;
* </pre>
*
* <p>
* Sort all files <code>*.txt</code> from <i>src</i> location using as
* sorting criterium <code>EvenFirstCmp</code> class, that sorts the file
* lines putting even lines first then odd lines for example. The modified files
* are copied into <i>build</i> location. The <code>EventFirstCmp</code>,
* has to an instanciable class via <code>Class.newInstance()</code>,
* therefore in case of inner class has to be <em>static</em>. It also has to
* implement <code>java.util.Comparator</code> interface, for example:
* </p>
*
* <pre>
* package org.apache.tools.ant.filters;
* ...(omitted)
* public final class EvenFirstCmp implements &lt;b&gt;Comparator&lt;/b&gt; {
* public int compare(Object o1, Object o2) {
* ...(omitted)
* }
* }
* </pre>
*
* <p> If parameter <code>comparator</code> is present, then
* <code>reverse</code> parameter will not taken into account. </p>
*
* @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 <code>java.util.Comparator</code> 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 <code>true</code>, then the line of the file will be
* sorted on descendant order. Default value: <code>false</code>. It will
* be considered only if <code>comparator</code> is <code>null</code>.
*/
private boolean reverse;

/**
* Stores the lines to be sorted.
*/
private List lines;

/**
* Remaining line to be read from this filter, or <code>null</code> if the
* next call to <code>read()</code> 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
* <code>null</code>.
*/
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
* <code>null</code>.
*
* @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 <code>true</code> if the sorting process will be in reverse
* order, otherwise the sorting process will be in ascendant order.
*
* @return <code>true</code> 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 (<code>reverse=false</code>)
* or to descendant (<code>reverse=true</code>).
*
* @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 (<code>lines</code>)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);
}
}
}

+ 10
- 0
src/tests/antunit/filters/expected/sort.sortComparator.test View File

@@ -0,0 +1,10 @@
Line 2
Line 4
Line 6
Line 8
Line 10
Line 1
Line 3
Line 5
Line 7
Line 9

+ 10
- 0
src/tests/antunit/filters/expected/sort.sortDefault.test View File

@@ -0,0 +1,10 @@
Line 1
Line 2
Line 3
Line 4
Line 5
Line 6
Line 7
Line 8
Line 9
Line 10

+ 10
- 0
src/tests/antunit/filters/expected/sort.sortReverse.test View File

@@ -0,0 +1,10 @@
Line 10
Line 9
Line 8
Line 7
Line 6
Line 5
Line 4
Line 3
Line 2
Line 1

+ 10
- 0
src/tests/antunit/filters/input/sort.sortDefault.test View File

@@ -0,0 +1,10 @@
Line 10
Line 1
Line 9
Line 6
Line 3
Line 2
Line 4
Line 5
Line 7
Line 8

+ 126
- 0
src/tests/antunit/filters/sort-test.xml View File

@@ -0,0 +1,126 @@
<?xml version="1.0"?>
<!--
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.
-->
<project default="antunit" xmlns:au="antlib:org.apache.ant.antunit">
<import file="../antunit-base.xml" />

<target name="setUp">
<mkdir dir="${output}"/>
</target>

<target name="testSortFilterNoArgs" depends="setUp">
<copy file="input/sort.sortDefault.test"
tofile="${output}/sort.sortDefault.test">
<filterchain>
<sortfilter/>
</filterchain>
</copy>
<au:assertFilesMatch
expected="expected/sort.sortDefault.test"
actual="${output}/sort.sortDefault.test"/>
</target>

<target name="testSortFilterNoArgsLong" depends="setUp">
<copy file="input/sort.sortDefault.test"
tofile="${output}/sort.sortDefault.test">
<filterchain>
<filterreader classname="org.apache.tools.ant.filters.SortFilter"/>
</filterchain>
</copy>
<au:assertFilesMatch
expected="expected/sort.sortDefault.test"
actual="${output}/sort.sortDefault.test"/>
</target>

<target name="testSortFilterReverse" depends="setUp">
<copy file="input/sort.sortDefault.test"
tofile="${output}/sort.sortReverse.test">
<filterchain>
<sortfilter reverse="true"/>
</filterchain>
</copy>
<au:assertFilesMatch
expected="expected/sort.sortReverse.test"
actual="${output}/sort.sortReverse.test"/>
</target>

<target name="testSortFilterReverseLong" depends="setUp">
<copy file="input/sort.sortDefault.test"
tofile="${output}/sort.sortReverse.test">
<filterchain>
<filterreader classname="org.apache.tools.ant.filters.SortFilter">
<param name="reverse" value="true"/>
</filterreader>
</filterchain>
</copy>
<au:assertFilesMatch
expected="expected/sort.sortReverse.test"
actual="${output}/sort.sortReverse.test"/>
</target>

<target name="-setUpEvenFirst" depends="setUp">
<mkdir dir="${input}/src/org/apache/tools/ant/filters"/>
<echo file="${input}/src/org/apache/tools/ant/filters/EvenFirstCmp.java"><![CDATA[
package org.apache.tools.ant.filters;

import java.util.Comparator;

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;
}
}
}
}
]]></echo>
<mkdir dir="${input}/build"/>
<javac srcdir="${input}/src" destdir="${input}/build"/>
<typedef classname="org.apache.tools.ant.filters.EvenFirstCmp"
name="evenfirst">
<classpath location="${input}/build"/>
</typedef>
</target>

<target name="testSortFilterComparator" depends="-setUpEvenFirst">
<copy file="input/sort.sortDefault.test"
tofile="${output}/sort.sortComparator.test">
<filterchain>
<sortfilter>
<evenfirst/>
</sortfilter>
</filterchain>
</copy>
<au:assertFilesMatch
expected="expected/sort.sortComparator.test"
actual="${output}/sort.sortComparator.test"/>
</target>
</project>

Loading…
Cancel
Save