git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@808009 13f79535-47bb-0310-9956-ffa450edef68master
@@ -73,6 +73,7 @@ David Gärtner | |||
David S. Johnson | |||
David Kavanagh | |||
David LeRoy | |||
David Leal | |||
David M. Lloyd | |||
David Maclean | |||
David Rees | |||
@@ -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 | |||
============================================= | |||
@@ -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> | |||
@@ -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> | |||
<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> | |||
</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> | |||
<filterreader classname="org.apache.tools.ant.filters.SortFilter"/> | |||
</pre></blockquote> | |||
Convenience method: | |||
<blockquote><pre> | |||
<sortfilter/> | |||
</pre></blockquote> | |||
This will reverse the sort order. | |||
<blockquote><pre> | |||
<filterreader classname="org.apache.tools.ant.filters.SortFilter"> | |||
<param name="reverse" value="true"/> | |||
</filterreader> | |||
</pre></blockquote> | |||
Convenience method: | |||
<blockquote><pre> | |||
<sortfilter reverse="true"/> | |||
</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> | |||
<typedef classname="org.apache.tools.ant.filters.EvenFirstCmp" | |||
name="evenfirst"/> | |||
... | |||
<filterchain> | |||
<sortfilter> | |||
<evenfirst/> | |||
</sortfilter> | |||
</filterchain> | |||
</pre></blockquote> | |||
</body></html> |
@@ -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> | |||
@@ -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> | |||
* <copy todir="build"> | |||
* <fileset dir="input" includes="*.txt"/> | |||
* <filterchain> | |||
* <sortfilter/> | |||
* </filterchain> | |||
* </copy> | |||
* </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> | |||
* <copy todir="build"> | |||
* <fileset dir="input" includes="*.txt"/> | |||
* <filterchain> | |||
* <sortfilter/> | |||
* <param name="reverse" value="true"/> | |||
* </sortfilter/> | |||
* </filterchain> | |||
* </copy> | |||
* </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> | |||
* <copy todir="build"> | |||
* <fileset dir="input" includes="*.txt"/> | |||
* <filterchain> | |||
* <sortfilter/> | |||
* <param name="comparator" value="org.apache.tools.ant.filters.EvenFirstCmp"/> | |||
* </sortfilter/> | |||
* </filterchain> | |||
* </copy> | |||
* </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 <b>Comparator</b> { | |||
* 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); | |||
} | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
Line 2 | |||
Line 4 | |||
Line 6 | |||
Line 8 | |||
Line 10 | |||
Line 1 | |||
Line 3 | |||
Line 5 | |||
Line 7 | |||
Line 9 |
@@ -0,0 +1,10 @@ | |||
Line 1 | |||
Line 2 | |||
Line 3 | |||
Line 4 | |||
Line 5 | |||
Line 6 | |||
Line 7 | |||
Line 8 | |||
Line 9 | |||
Line 10 |
@@ -0,0 +1,10 @@ | |||
Line 10 | |||
Line 9 | |||
Line 8 | |||
Line 7 | |||
Line 6 | |||
Line 5 | |||
Line 4 | |||
Line 3 | |||
Line 2 | |||
Line 1 |
@@ -0,0 +1,10 @@ | |||
Line 10 | |||
Line 1 | |||
Line 9 | |||
Line 6 | |||
Line 3 | |||
Line 2 | |||
Line 4 | |||
Line 5 | |||
Line 7 | |||
Line 8 |
@@ -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> |