/* * 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 and copy * them into build location. The lines of each file are sorted * in ascendant order comparing the lines via the * String.compareTo(Object o) method. *

* *
 *   <copy todir="build">
 *       <fileset dir="input" includes="*.txt"/>
 *       <filterchain>
 *           <sortfilter reverse="true"/>
 *       </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>
 *           <filterreader classname="org.apache.tools.ant.filters.SortFilter">
 *             <param name="comparator" value="org.apache.tools.ant.filters.EvenFirstCmp"/>
 *           </filterreader>
 *       </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 EvenFirstCmp, * 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)
 *             }
 *           }
 * 
* *

The example above is equivalent to:

* *
 *   <componentdef name="evenfirst"
 *                 classname="org.apache.tools.ant.filters.EvenFirstCmp"/>
 *   <copy todir="build">
 *       <fileset dir="input" includes="*.txt"/>
 *       <filterchain>
 *           <sortfilter>
 *               <evenfirst/>
 *           </sortfilter>
 *       </filterchain>
 *   </copy>
 * 
* *

If parameter comparator is present, then * reverse parameter will not be 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 criterion 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(); @SuppressWarnings("unchecked") final Comparator comparatorInstance = (Comparator) (Class .forName(className).newInstance()); setComparator(comparatorInstance); 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) according to the sorting * criteria defined by the user. * */ private void sort() { if (comparator == null) { if (reverse) { Collections.sort(lines, new Comparator() { public int compare(String s1, String s2) { return (-s1.compareTo(s2)); } }); } else { Collections.sort(lines); } } else { Collections.sort(lines, comparator); } } }