| /* |
| * 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 |
| * |
| * https://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 and copy |
| * them into <i>build</i> location. The lines of each file are sorted |
| * in ascendant order comparing the lines via the |
| * <code>String.compareTo(Object o)</code> method. |
| * </p> |
| * |
| * <pre> |
| * <copy todir="build"> |
| * <fileset dir="input" includes="*.txt"/> |
| * <filterchain> |
| * <sortfilter reverse="true"/> |
| * </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> |
| * <filterreader classname="org.apache.tools.ant.filters.SortFilter"> |
| * <param name="comparator" value="org.apache.tools.ant.filters.EvenFirstCmp"/> |
| * </filterreader> |
| * </filterchain> |
| * </copy> |
| * </pre> |
| * |
| * <p> |
| * Sort all files <code>*.txt</code> from <i>src</i> location using as |
| * sorting criterion <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>EvenFirstCmp</code>, |
| * has to an instantiable 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>The example above is equivalent to:</p> |
| * |
| * <pre> |
| * <componentdef name="evenfirst" |
| * classname="org.apache.tools.ant.filters.EvenFirstCmp"/> |
| * <copy todir="build"> |
| * <fileset dir="input" includes="*.txt"/> |
| * <filterchain> |
| * <sortfilter> |
| * <evenfirst/> |
| * </sortfilter> |
| * </filterchain> |
| * </copy> |
| * </pre> |
| * |
| * <p>If parameter <code>comparator</code> is present, then |
| * <code>reverse</code> parameter will not be 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<? super String> 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<String> 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<String> 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 = 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<? super String> getComparator() { |
| return comparator; |
| } |
| |
| /** |
| * Set the comparator to be used as sorting criterion. |
| * |
| * @param comparator |
| * the comparator to set |
| */ |
| public void setComparator(Comparator<? super String> 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<? super String> 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() { |
| // get parameters |
| Parameter[] params = getParameters(); |
| if (params != null) { |
| for (Parameter param : params) { |
| final String paramName = param.getName(); |
| if (REVERSE_KEY.equals(paramName)) { |
| setReverse(Boolean.valueOf(param.getValue())); |
| } else if (COMPARATOR_KEY.equals(paramName)) { |
| try { |
| String className = param.getValue(); |
| @SuppressWarnings("unchecked") |
| final Comparator<? super String> comparatorInstance |
| = (Comparator<? super String>) (Class.forName(className).getDeclaredConstructor().newInstance()); |
| setComparator(comparatorInstance); |
| } catch (ClassCastException e) { |
| throw new BuildException("Value of comparator attribute" |
| + " should implement" |
| + " java.util.Comparator" |
| + " interface"); |
| } catch (Exception e) { |
| /* |
| * IAE probably means an inner non-static class, that case is not considered |
| */ |
| throw new BuildException(e); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Sorts the read lines (<code>lines</code>) according to the sorting |
| * criteria defined by the user. |
| * |
| */ |
| private void sort() { |
| if (comparator == null) { |
| if (isReverse()) { |
| lines.sort(Comparator.reverseOrder()); |
| } else { |
| Collections.sort(lines); |
| } |
| } else { |
| lines.sort(comparator); |
| } |
| } |
| } |