001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 * 
016 * Contributors:
017 *     bstefanescu
018 *
019 * $Id$
020 */
021
022package org.nuxeo.common.utils;
023
024import java.io.File;
025import java.io.FileFilter;
026import java.util.Iterator;
027import java.util.LinkedList;
028import java.util.NoSuchElementException;
029import java.util.Queue;
030
031/**
032 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
033 */
034public class FileTreeIterator implements Iterator<File> {
035
036    private final Queue<Iterator<File>> queue = new LinkedList<Iterator<File>>();
037
038    private File file; // last iterated file
039
040    private FileFilter filter;
041
042    public FileTreeIterator(File root) {
043        queue.add(new OneFileIterator(root));
044    }
045
046    public FileTreeIterator(File root, boolean excludeRoot) {
047        if (excludeRoot) {
048            queue.add(new LazyChildrenIterator(root));
049        } else {
050            queue.add(new OneFileIterator(root));
051        }
052    }
053
054    public void setFilter(FileFilter filter) {
055        this.filter = filter;
056    }
057
058    public FileFilter getFilter() {
059        return filter;
060    }
061
062    public boolean hasNext() {
063        if (queue.isEmpty()) {
064            return false;
065        }
066        Iterator<File> it = queue.peek();
067        if (it.hasNext()) {
068            return true;
069        } else {
070            queue.poll();
071            return hasNext();
072        }
073    }
074
075    public File next() {
076        if (!hasNext()) {
077            throw new NoSuchElementException("No more files to iterate over");
078        }
079        file = queue.peek().next();
080        if (file.isDirectory()) {
081            queue.add(new LazyChildrenIterator(file));
082        }
083        return file;
084    }
085
086    public void remove() {
087        if (file == null) {
088            throw new IllegalStateException("there is no current file to delete");
089        }
090        if (file.isDirectory()) {
091            FileUtils.deleteTree(file);
092        } else {
093            file.delete();
094        }
095    }
096
097    // we don't fulfill iterator contract - we don't need a real iterator,
098    // this is used only internally
099    private static class OneFileIterator implements Iterator<File> {
100
101        private File file;
102
103        private OneFileIterator(File file) {
104            this.file = file;
105        }
106
107        public boolean hasNext() {
108            return file != null;
109        }
110
111        public File next() {
112            File next = file;
113            file = null;
114            return next;
115        }
116
117        public void remove() {
118            throw new UnsupportedOperationException("remove not supported");
119        }
120    }
121
122    // we don't fulfill iterator contract - we don't need a real iterator,
123    // this is used only internally
124    private class LazyChildrenIterator implements Iterator<File> {
125
126        private final File dir;
127
128        private File[] children;
129
130        private int pos = -1; // last pos
131
132        private LazyChildrenIterator(File dir) {
133            this.dir = dir;
134        }
135
136        public boolean hasNext() {
137            if (children == null) {
138                children = filter == null ? dir.listFiles() : dir.listFiles(filter);
139                if (children == null) {
140                    return false; // not a dir
141                }
142                return children.length > 0;
143            } else {
144                return pos < children.length - 1;
145            }
146        }
147
148        public File next() {
149            return children[++pos];
150        }
151
152        public void remove() {
153            throw new UnsupportedOperationException("remove not supported");
154        }
155    }
156
157    public static void main(String[] args) {
158        FileTreeIterator it = new FileTreeIterator(new File("/root/kits"), false);
159        while (it.hasNext()) {
160            System.out.println(">> " + it.next());
161        }
162    }
163
164}