• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.tools;
18 
19 import com.badlogic.gdx.utils.Array;
20 
21 import java.io.File;
22 import java.io.FilenameFilter;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.LinkedHashMap;
27 import java.util.regex.Pattern;
28 
29 /** Collects files recursively, filtering by file name. Callbacks are provided to process files and the results are collected,
30  * either {@link #processFile(Entry)} or {@link #processDir(Entry, ArrayList)} can be overridden, or both. The entries provided to
31  * the callbacks have the original file, the output directory, and the output file. If {@link #setFlattenOutput(boolean)} is
32  * false, the output will match the directory structure of the input.
33  * @author Nathan Sweet */
34 public class FileProcessor {
35 	FilenameFilter inputFilter;
36 	Comparator<File> comparator = new Comparator<File>() {
37 		public int compare (File o1, File o2) {
38 			return o1.getName().compareTo(o2.getName());
39 		}
40 	};
41 	Array<Pattern> inputRegex = new Array();
42 	String outputSuffix;
43 	ArrayList<Entry> outputFiles = new ArrayList();
44 	boolean recursive = true;
45 	boolean flattenOutput;
46 
47 	Comparator<Entry> entryComparator = new Comparator<Entry>() {
48 		public int compare (Entry o1, Entry o2) {
49 			return comparator.compare(o1.inputFile, o2.inputFile);
50 		}
51 	};
52 
FileProcessor()53 	public FileProcessor () {
54 	}
55 
56 	/** Copy constructor. */
FileProcessor(FileProcessor processor)57 	public FileProcessor (FileProcessor processor) {
58 		inputFilter = processor.inputFilter;
59 		comparator = processor.comparator;
60 		inputRegex.addAll(processor.inputRegex);
61 		outputSuffix = processor.outputSuffix;
62 		recursive = processor.recursive;
63 		flattenOutput = processor.flattenOutput;
64 	}
65 
setInputFilter(FilenameFilter inputFilter)66 	public FileProcessor setInputFilter (FilenameFilter inputFilter) {
67 		this.inputFilter = inputFilter;
68 		return this;
69 	}
70 
71 	/** Sets the comparator for {@link #processDir(Entry, ArrayList)}. By default the files are sorted by alpha. */
setComparator(Comparator<File> comparator)72 	public FileProcessor setComparator (Comparator<File> comparator) {
73 		this.comparator = comparator;
74 		return this;
75 	}
76 
77 	/** Adds a case insensitive suffix for matching input files. */
addInputSuffix(String... suffixes)78 	public FileProcessor addInputSuffix (String... suffixes) {
79 		for (String suffix : suffixes)
80 			addInputRegex("(?i).*" + Pattern.quote(suffix));
81 		return this;
82 	}
83 
addInputRegex(String... regexes)84 	public FileProcessor addInputRegex (String... regexes) {
85 		for (String regex : regexes)
86 			inputRegex.add(Pattern.compile(regex));
87 		return this;
88 	}
89 
90 	/** Sets the suffix for output files, replacing the extension of the input file. */
setOutputSuffix(String outputSuffix)91 	public FileProcessor setOutputSuffix (String outputSuffix) {
92 		this.outputSuffix = outputSuffix;
93 		return this;
94 	}
95 
setFlattenOutput(boolean flattenOutput)96 	public FileProcessor setFlattenOutput (boolean flattenOutput) {
97 		this.flattenOutput = flattenOutput;
98 		return this;
99 	}
100 
101 	/** Default is true. */
setRecursive(boolean recursive)102 	public FileProcessor setRecursive (boolean recursive) {
103 		this.recursive = recursive;
104 		return this;
105 	}
106 
107 	/** @param outputRoot May be null.
108 	 * @see #process(File, File) */
process(String inputFileOrDir, String outputRoot)109 	public ArrayList<Entry> process (String inputFileOrDir, String outputRoot) throws Exception {
110 		return process(new File(inputFileOrDir), outputRoot == null ? null : new File(outputRoot));
111 	}
112 
113 	/** Processes the specified input file or directory.
114 	 * @param outputRoot May be null if there is no output from processing the files.
115 	 * @return the processed files added with {@link #addProcessedFile(Entry)}. */
process(File inputFileOrDir, File outputRoot)116 	public ArrayList<Entry> process (File inputFileOrDir, File outputRoot) throws Exception {
117 		if (!inputFileOrDir.exists()) throw new IllegalArgumentException("Input file does not exist: " + inputFileOrDir.getAbsolutePath());
118 		if (inputFileOrDir.isFile())
119 			return process(new File[] {inputFileOrDir}, outputRoot);
120 		else
121 			return process(inputFileOrDir.listFiles(), outputRoot);
122 	}
123 
124 	/** Processes the specified input files.
125 	 * @param outputRoot May be null if there is no output from processing the files.
126 	 * @return the processed files added with {@link #addProcessedFile(Entry)}. */
process(File[] files, File outputRoot)127 	public ArrayList<Entry> process (File[] files, File outputRoot) throws Exception {
128 		if (outputRoot == null) outputRoot = new File("");
129 		outputFiles.clear();
130 
131 		LinkedHashMap<File, ArrayList<Entry>> dirToEntries = new LinkedHashMap();
132 		process(files, outputRoot, outputRoot, dirToEntries, 0);
133 
134 		ArrayList<Entry> allEntries = new ArrayList();
135 		for (java.util.Map.Entry<File, ArrayList<Entry>> mapEntry : dirToEntries.entrySet()) {
136 			ArrayList<Entry> dirEntries = mapEntry.getValue();
137 			if (comparator != null) Collections.sort(dirEntries, entryComparator);
138 
139 			File inputDir = mapEntry.getKey();
140 			File newOutputDir = null;
141 			if (flattenOutput)
142 				newOutputDir = outputRoot;
143 			else if (!dirEntries.isEmpty()) //
144 				newOutputDir = dirEntries.get(0).outputDir;
145 			String outputName = inputDir.getName();
146 			if (outputSuffix != null) outputName = outputName.replaceAll("(.*)\\..*", "$1") + outputSuffix;
147 
148 			Entry entry = new Entry();
149 			entry.inputFile = mapEntry.getKey();
150 			entry.outputDir = newOutputDir;
151 			if (newOutputDir != null)
152 				entry.outputFile = newOutputDir.length() == 0 ? new File(outputName) : new File(newOutputDir, outputName);
153 
154 			try {
155 				processDir(entry, dirEntries);
156 			} catch (Exception ex) {
157 				throw new Exception("Error processing directory: " + entry.inputFile.getAbsolutePath(), ex);
158 			}
159 			allEntries.addAll(dirEntries);
160 		}
161 
162 		if (comparator != null) Collections.sort(allEntries, entryComparator);
163 		for (Entry entry : allEntries) {
164 			try {
165 				processFile(entry);
166 			} catch (Exception ex) {
167 				throw new Exception("Error processing file: " + entry.inputFile.getAbsolutePath(), ex);
168 			}
169 		}
170 
171 		return outputFiles;
172 	}
173 
process(File[] files, File outputRoot, File outputDir, LinkedHashMap<File, ArrayList<Entry>> dirToEntries, int depth)174 	private void process (File[] files, File outputRoot, File outputDir, LinkedHashMap<File, ArrayList<Entry>> dirToEntries,
175 		int depth) {
176 		// Store empty entries for every directory.
177 		for (File file : files) {
178 			File dir = file.getParentFile();
179 			ArrayList<Entry> entries = dirToEntries.get(dir);
180 			if (entries == null) {
181 				entries = new ArrayList();
182 				dirToEntries.put(dir, entries);
183 			}
184 		}
185 
186 		for (File file : files) {
187 			if (file.isFile()) {
188 				if (inputRegex.size > 0) {
189 					boolean found = false;
190 					for (Pattern pattern : inputRegex) {
191 						if (pattern.matcher(file.getName()).matches()) {
192 							found = true;
193 							continue;
194 						}
195 					}
196 					if (!found) continue;
197 				}
198 
199 				File dir = file.getParentFile();
200 				if (inputFilter != null && !inputFilter.accept(dir, file.getName())) continue;
201 
202 				String outputName = file.getName();
203 				if (outputSuffix != null) outputName = outputName.replaceAll("(.*)\\..*", "$1") + outputSuffix;
204 
205 				Entry entry = new Entry();
206 				entry.depth = depth;
207 				entry.inputFile = file;
208 				entry.outputDir = outputDir;
209 
210 				if (flattenOutput) {
211 					entry.outputFile = new File(outputRoot, outputName);
212 				} else {
213 					entry.outputFile = new File(outputDir, outputName);
214 				}
215 
216 				dirToEntries.get(dir).add(entry);
217 			}
218 			if (recursive && file.isDirectory()) {
219 				File subdir = outputDir.getPath().length() == 0 ? new File(file.getName()) : new File(outputDir, file.getName());
220 				process(file.listFiles(inputFilter), outputRoot, subdir, dirToEntries, depth + 1);
221 			}
222 		}
223 	}
224 
225 	/** Called with each input file. */
processFile(Entry entry)226 	protected void processFile (Entry entry) throws Exception {
227 	}
228 
229 	/** Called for each input directory. The files will be {@link #setComparator(Comparator) sorted}. */
processDir(Entry entryDir, ArrayList<Entry> files)230 	protected void processDir (Entry entryDir, ArrayList<Entry> files) throws Exception {
231 	}
232 
233 	/** This method should be called by {@link #processFile(Entry)} or {@link #processDir(Entry, ArrayList)} if the return value of
234 	 * {@link #process(File, File)} or {@link #process(File[], File)} should return all the processed files. */
addProcessedFile(Entry entry)235 	protected void addProcessedFile (Entry entry) {
236 		outputFiles.add(entry);
237 	}
238 
239 	/** @author Nathan Sweet */
240 	static public class Entry {
241 		public File inputFile;
242 		/** May be null. */
243 		public File outputDir;
244 		public File outputFile;
245 		public int depth;
246 
Entry()247 		public Entry () {
248 		}
249 
Entry(File inputFile, File outputFile)250 		public Entry (File inputFile, File outputFile) {
251 			this.inputFile = inputFile;
252 			this.outputFile = outputFile;
253 		}
254 
toString()255 		public String toString () {
256 			return inputFile.toString();
257 		}
258 	}
259 }
260