001: /*
002: * MeshCMS - A simple CMS based on SiteMesh
003: * Copyright (C) 2004-2007 Luciano Vernaschi
004: *
005: * This program is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU General Public License
007: * as published by the Free Software Foundation; either version 2
008: * of the License, or (at your option) any later version.
009: *
010: * This program is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013: * GNU General Public License for more details.
014: *
015: * You should have received a copy of the GNU General Public License
016: * along with this program; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018: *
019: * You can contact the author at http://www.cromoteca.com
020: * and at info@cromoteca.com
021: */
022:
023: package org.meshcms.util;
024:
025: import java.io.*;
026: import java.util.*;
027:
028: /**
029: * Base class to perform operations on the contents of a directory.
030: *
031: * <p>Override the method <code>preProcessDirectory</code>,
032: * <code>postProcessDirectory</code> and
033: * <code>processFile</code> to define the actions to be taken for files and
034: * directories included in the processed directory. You can also override
035: * <code>preProcess</code> and <code>postProcess</code> to do additional
036: * operations before and after the directory parsing.</p>
037: *
038: * <p>The directory to be parsed must be specified before starting by calling
039: * on of the <code>setInitialDir</code> methods. Then you can start the parsing
040: * by calling <code>process</code> or asinchronously by creating a new thread
041: * and starting it.</p>
042: *
043: * <p>Please note that this class is <em>not</em> recursive by default. You must
044: * call <code>setRecursive(true)</code> before processing if you want it to
045: * process directory contents too.</p>
046: *
047: * @author Luciano Vernaschi
048: */
049: public class DirectoryParser extends Thread {
050: protected File initialDir;
051:
052: protected boolean recursive = false;
053: protected boolean processStartDir = false;
054: protected boolean processDirBeforeContent = true;
055:
056: private Comparator comparator;
057:
058: /**
059: * If true, directories will be processed recursively (default false).
060: *
061: * @param recursive it the directories will be processed recursively
062: */
063: public void setRecursive(boolean recursive) {
064: this .recursive = recursive;
065: }
066:
067: /**
068: * If true, <code>processDirectory</code> will be called for the
069: * base directory too (default false).
070: *
071: * @param processStartDir if to process the base directory too.
072: *
073: * @see #processDirectory
074: */
075: public void setProcessStartDir(boolean processStartDir) {
076: this .processStartDir = processStartDir;
077: }
078:
079: /**
080: * If true, <code>processDirectory</code> will be called
081: * for a directory before processing its contents (default true).
082: *
083: * @see #processDirectory
084: * @deprecated implement {@link #preProcessDirectory} or {@link #postProcessDirectory} accordingly
085: */
086: public void setProcessDirBeforeContent(
087: boolean processDirBeforeContent) {
088: this .processDirBeforeContent = processDirBeforeContent;
089: }
090:
091: /**
092: * Returns whether directories will be processed recursively or not.
093: *
094: * @see #setRecursive
095: */
096: public boolean isRecursive() {
097: return recursive;
098: }
099:
100: /**
101: * Returns whether <code>processDirectory</code> will be called for the
102: * base directory too.
103: *
104: * @see #processDirectory
105: * @see #setProcessStartDir
106: */
107: public boolean isProcessStartDir() {
108: return processStartDir;
109: }
110:
111: /**
112: * Returns whether <code>processDirectory</code> will be called
113: * for a directory before processing its contents.
114: *
115: * @see #processDirectory
116: * @see #setProcessDirBeforeContent
117: *
118: * @deprecated implement {@link #preProcessDirectory} or {@link #postProcessDirectory} accordingly
119: */
120: public boolean isProcessDirBeforeContent() {
121: return processDirBeforeContent;
122: }
123:
124: /**
125: * If true, files and directories will be sorted used a
126: * <code>FileNameComparator</code>.
127: *
128: * @see FileNameComparator
129: */
130: public void setSorted(boolean sorted) {
131: if (sorted) {
132: comparator = new FileNameComparator();
133: } else {
134: comparator = null;
135: }
136: }
137:
138: /**
139: * Returns whether files and directories will be sorted used a
140: * <code>FileNameComparator</code> or not.
141: *
142: * @see FileNameComparator
143: * @see #setSorted
144: */
145: public boolean isSorted() {
146: return comparator != null;
147: }
148:
149: /**
150: * Sets the directory to be processed. An istance of <code>File</code> is
151: * created and {@link #setInitialDir(File)} is called.
152: *
153: * @param dir the file path as a <code>String</code>
154: */
155: public void setInitialDir(String dir) {
156: setInitialDir(Utils.isNullOrEmpty(dir) ? null : new File(dir));
157: }
158:
159: /**
160: * Sets the directory to be processed.
161: *
162: * @param dir the directory path as a <code>File</code>
163: */
164: public void setInitialDir(File dir) {
165: initialDir = dir;
166: }
167:
168: /**
169: * Returns the directory to be processed.
170: */
171: public File getInitialDir() {
172: return initialDir;
173: }
174:
175: /**
176: * Starts processing (in a separate thread if instantiated properly).
177: */
178: public void run() {
179: process();
180: }
181:
182: /**
183: * Starts processing.
184: */
185: public void process() {
186: if (initialDir == null || !initialDir.exists()) {
187: return;
188: }
189:
190: if (preProcess()) {
191: parse(initialDir, Path.ROOT);
192: }
193:
194: postProcess();
195: }
196:
197: private void parse(File file, Path path) {
198: if (file.isDirectory()) {
199: if (recursive || path.getElementCount() == 0) {
200: boolean ok = true;
201:
202: if (mustProcessDir(path)) {
203: ok = preProcessDirectory(file, path);
204:
205: // support deprecated processDirBeforeContent
206: if (processDirBeforeContent) {
207: ok &= processDirectory(file, path);
208: }
209: }
210:
211: if (ok) {
212: File[] list = file.listFiles();
213:
214: if (comparator != null) {
215: Arrays.sort(list, comparator);
216: }
217:
218: for (int i = 0; i < list.length; i++) {
219: parse(list[i], path.add(list[i].getName()));
220: }
221: }
222: }
223:
224: if (mustProcessDir(path)) {
225: postProcessDirectory(file, path);
226:
227: // support deprecated processDirBeforeContent
228: if (!processDirBeforeContent) {
229: processDirectory(file, path);
230: }
231: }
232: } else if (file.isFile()) {
233: processFile(file, path);
234: }
235: }
236:
237: private boolean mustProcessDir(Path path) {
238: return processStartDir || path.getElementCount() != 0;
239: }
240:
241: /**
242: * This method is called during the process, but before any element has been
243: * processed. If it returns false, no processing will take place.
244: *
245: * <p>The base implementation does nothing and returns true.</p>
246: *
247: * @return always true
248: */
249: protected boolean preProcess() {
250: return true;
251: }
252:
253: /**
254: * This method is called at the end of the processing. It is called even if
255: * {@link #preProcess} returned false.
256: *
257: * <p>The base implementation does nothing.</p>
258: */
259: protected void postProcess() {
260: }
261:
262: /**
263: * This method will be called for any directory found while parsing the base
264: * directory. You can return false to block processing the contents of the
265: * directory (provided that {@link #isProcessDirBeforeContent} returns true,
266: * otherwise contents have been processed already).
267: *
268: * @param file the directory to be processed
269: * @param path the path of the directory (relative to the base directory)
270: *
271: * @return always true
272: *
273: * @deprecated use {@link #preProcessDirectory} and {@link #postProcessDirectory} instead
274: */
275: protected boolean processDirectory(File file, Path path) {
276: return true;
277: }
278:
279: protected boolean preProcessDirectory(File file, Path path) {
280: return true;
281: }
282:
283: protected void postProcessDirectory(File file, Path path) {
284: }
285:
286: /**
287: * This method will be called for any file found while parsing the base
288: * directory.
289: *
290: * @param file the file to be processed
291: * @param path the path of the file (relative to the base directory)
292: */
293: protected void processFile(File file, Path path) {
294: }
295: }
|