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    *      http://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  
18  package org.apache.commons.imaging.examples.tiff;
19  
20  import java.io.BufferedOutputStream;
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.PrintStream;
25  import java.nio.charset.StandardCharsets;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Comparator;
29  import java.util.List;
30  
31  /**
32   * Recursively search the specified path and list TIFF files and metadata.
33   * <p>
34   * Command-line Arguments:
35   * </p>
36   * <ol>
37   * <li>Top-level directory (mandatory)</li>
38   * <li>Output file for results (optional)</li>
39   * </ol>
40   * If the optional output file has the extension ".csv", the output will be formatted as a comma-separated-value file suitable for inspection in Excel.
41   */
42  public class SurveyTiffFolder {
43  
44      private static final class PathComparator implements Comparator<String[]> {
45  
46          @Override
47          public int compare(final String[] a, final String[] b) {
48              for (int i = 0; i < a.length && i < b.length; i++) {
49                  int test;
50                  if (isNumeric(a[i]) && isNumeric(b[i])) {
51                      final int iA = Integer.parseInt(a[i]);
52                      final int iB = Integer.parseInt(b[i]);
53                      test = iA - iB;
54                  } else {
55                      test = a[i].compareTo(b[i]);
56                  }
57                  if (test != 0) {
58                      return test;
59                  }
60              }
61              // in practice, the program should never reach this position.
62              // at this point, all entries in both arrays are equal,
63              // so order the entries so that the shortest array goes first
64              if (a.length < b.length) {
65                  return -1;
66              }
67              return 1;
68          }
69  
70          private boolean isNumeric(final String a) {
71              for (int i = 0; i < a.length(); i++) {
72                  if (!Character.isDigit(a.charAt(i))) {
73                      return false;
74                  }
75              }
76              return true;
77          }
78  
79      }
80  
81      private static int collectPaths(final File parent, final List<String[]> pathList, final String[] scratch, final int depth) {
82          if (depth == scratch.length) {
83              // directory hierarchy is too deep
84              return 0;
85          }
86  
87          final File[] files = parent.listFiles();
88          for (final File f : files) {
89              if (!f.isHidden()) {
90                  final String name = f.getName();
91                  scratch[depth] = name;
92                  if (f.isDirectory()) {
93                      collectPaths(f, pathList, scratch, depth + 1);
94                  } else {
95                      final int i = name.lastIndexOf('.');
96                      if (i > 0) {
97                          final String ext = name.substring(i).toLowerCase();
98                          if (".tif".equals(ext) || ".tiff".equals(ext)) {
99                              final String[] temp = Arrays.copyOf(scratch, depth + 1);
100                             pathList.add(temp);
101                         }
102                     }
103                 }
104             }
105         }
106         return depth;
107     }
108 
109     private static int[] findMaxLengths(final List<String[]> pathList) {
110         int[] m = new int[1];
111         for (final String[] s : pathList) {
112             if (s.length > m.length) {
113                 m = Arrays.copyOf(m, s.length);
114             }
115             for (int i = 0; i < s.length; i++) {
116                 if (s[i].length() > m[i]) {
117                     m[i] = s[i].length();
118                 }
119             }
120         }
121         return m;
122     }
123 
124     /**
125      * @param args the command line arguments
126      */
127     public static void main(final String[] args) {
128         if (args.length < 1) {
129             System.err.println("Missing directory path");
130             System.exit(-1);
131         }
132         final File topLevelDir = new File(args[0]);
133         if (!topLevelDir.isDirectory() || !topLevelDir.canRead()) {
134             System.err.println("Path specification is not an accessible directory " + args[0]);
135             System.exit(-1);
136         }
137 
138         // recursively survey file paths
139         final String[] scratch = new String[256];
140         final List<String[]> pathList = new ArrayList<>();
141         collectPaths(topLevelDir, pathList, scratch, 0);
142         pathList.sort(new PathComparator());
143 
144         // find maximum lengths of each entry
145         final int[] maxLen = findMaxLengths(pathList);
146 
147         // If args.length is 1, write report to System.out,
148         // otherwise, write to a file.
149         if (args.length == 1) {
150             surveyFiles(topLevelDir, pathList, maxLen, false, System.out);
151         } else {
152 
153             boolean csv = false;
154 
155             final int i = args[1].lastIndexOf('.');
156             if (i > 0) {
157                 final String ext = args[1].substring(i);
158                 if (".csv".equalsIgnoreCase(ext)) {
159                     csv = true;
160                 }
161             }
162             final File reportFile = new File(args[1]);
163             try (FileOutputStream fos = new FileOutputStream(reportFile);
164                     BufferedOutputStream bos = new BufferedOutputStream(fos);
165                     PrintStream ps = new PrintStream(bos, true, StandardCharsets.UTF_8.name())) {
166                 surveyFiles(topLevelDir, pathList, maxLen, csv, ps);
167             } catch (final IOException ioex) {
168                 System.err.println("IOException writing report to " + args[1]);
169                 System.err.println("" + ioex.getMessage());
170             }
171         }
172     }
173 
174     private static void surveyFiles(final File topDir, final List<String[]> pathList, final int[] maxLen, final boolean csv, final PrintStream ps) {
175         final SurveyTiffFile surveyor = new SurveyTiffFile();
176         int n = maxLen.length - 1;
177         for (final int element : maxLen) {
178             n += element;
179         }
180         if (n < 10) {
181             n = 10;
182         }
183 
184         final String header = surveyor.formatHeader(n, csv);
185         ps.println(header);
186 
187         final List<String> badFiles = new ArrayList<>();
188         for (final String[] path : pathList) {
189             final StringBuilder sBuilder = new StringBuilder();
190             File file = topDir;
191             for (final String s : path) {
192                 file = new File(file, s);
193             }
194             for (int i = 0; i < path.length; i++) {
195                 if (i > 0) {
196                     sBuilder.append('/');
197                 }
198                 sBuilder.append(path[i]);
199             }
200             if (!csv) {
201                 for (int i = sBuilder.length(); i < n; i++) {
202                     sBuilder.append(' ');
203                 }
204             }
205 
206             String result;
207             try {
208                 result = surveyor.surveyFile(file, csv);
209             } catch (final IOException ex) {
210                 sBuilder.append(ex.getMessage());
211                 badFiles.add(sBuilder.toString());
212                 continue; // result = ex.getMessage();
213             }
214             sBuilder.append(result);
215             ps.println(sBuilder.toString());
216         }
217         if (!csv && !badFiles.isEmpty()) {
218             ps.println();
219             ps.println("Bad Files:");
220             for (final String s : badFiles) {
221                 ps.println(s);
222             }
223         }
224 
225         if (!csv) {
226             ps.println();
227             surveyor.printLegend(ps);
228         }
229     }
230 }