View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.io.comparator;
18  
19  import java.io.File;
20  import java.io.Serializable;
21  import java.util.Arrays;
22  import java.util.Comparator;
23  import java.util.function.IntFunction;
24  import java.util.stream.Stream;
25  import java.util.stream.StreamSupport;
26  
27  /**
28   * Compare two files using a set of delegate file {@link Comparator}.
29   * <p>
30   * This comparator can be used to sort lists or arrays of files by combining a number of other comparators.
31   * <p>
32   * Example of sorting a list of files by type (directory or file) and then by name:
33   *
34   * <pre>
35   *       CompositeFileComparator comparator = new CompositeFileComparator(
36   *           DirectoryFileComparator.DIRECTORY_COMPARATOR,
37   *           NameFileComparator.NAME_COMPARATOR);
38   *       List&lt;File&gt; list = ...
39   *       comparator.sort(list);
40   * </pre>
41   * <h2>Deprecating Serialization</h2>
42   * <p>
43   * <em>Serialization is deprecated and will be removed in 3.0.</em>
44   * </p>
45   *
46   * @since 2.0
47   */
48  public class CompositeFileComparator extends AbstractFileComparator implements Serializable {
49  
50      private static final Comparator<?>[] EMPTY_COMPARATOR_ARRAY = {};
51      private static final long serialVersionUID = -2224170307287243428L;
52  
53      /**
54       * Delegates.
55       */
56      private final Comparator<File>[] delegates;
57  
58      /**
59       * Constructs a composite comparator for the set of delegate comparators.
60       *
61       * @param delegates The delegate file comparators
62       */
63      public CompositeFileComparator(@SuppressWarnings("unchecked") final Comparator<File>... delegates) {
64          this.delegates = delegates == null ? emptyArray() : delegates.clone();
65      }
66  
67      /**
68       * Constructs a composite comparator for the set of delegate comparators.
69       *
70       * @param delegates The delegate file comparators
71       */
72      public CompositeFileComparator(final Iterable<Comparator<File>> delegates) {
73          this.delegates = delegates == null ? emptyArray()
74                  : StreamSupport.stream(delegates.spliterator(), false).toArray((IntFunction<Comparator<File>[]>) Comparator[]::new);
75      }
76  
77      /**
78       * Compares the two files using delegate comparators.
79       *
80       * @param file1 The first file to compare
81       * @param file2 The second file to compare
82       * @return the first non-zero result returned from the delegate comparators or zero.
83       */
84      @Override
85      public int compare(final File file1, final File file2) {
86          return Stream.of(delegates).map(delegate -> delegate.compare(file1, file2)).filter(r -> r != 0).findFirst().orElse(0);
87      }
88  
89      @SuppressWarnings("unchecked") // types are already correct
90      private Comparator<File>[] emptyArray() {
91          return (Comparator<File>[]) EMPTY_COMPARATOR_ARRAY;
92      }
93  
94      /**
95       * String representation of this file comparator.
96       *
97       * @return String representation of this file comparator
98       */
99      @Override
100     public String toString() {
101         final StringBuilder builder = new StringBuilder(super.toString());
102         builder.append(Arrays.toString(delegates));
103         return builder.toString();
104     }
105 }