001/**
002 * The contents of this file are subject to the license and copyright
003 * detailed in the LICENSE and NOTICE files at the root of the source
004 * tree.
005 *
006 */
007package org.fcrepo.migration.foxml;
008
009import java.io.File;
010import java.io.FileFilter;
011import java.io.FileNotFoundException;
012import java.util.ArrayList;
013import java.util.Arrays;
014import java.util.Iterator;
015import java.util.List;
016import java.util.Stack;
017
018import javax.xml.stream.XMLStreamException;
019
020import org.fcrepo.migration.FedoraObjectProcessor;
021
022/**
023 * A depth-first-search iteration over a tree of files that exposes them as FedoraObjectProcessors.
024 * Each file in the tree is expected to be a FOXML file.   This implementation likely minimizes
025 * memory usage for the expected organization of FOXML files on disk.
026 * @author mdurbin
027 */
028public class FoxmlDirectoryDFSIterator implements Iterator<FedoraObjectProcessor> {
029
030    private List<File> current;
031    private Stack<List<File>> stack;
032
033    private InternalIDResolver resolver;
034    private URLFetcher fetcher;
035
036    private String localFedoraServer;
037
038    private FileFilter fileFilter;
039
040    /**
041     * foxml directory DFS iterator.
042     * @param root the root file
043     * @param fetcher the fetcher
044     * @param localFedoraServer uri to local fedora server
045     * @param fileFilter a FileFilter that defined which files should be included
046     *        in this Iterator.
047     */
048    public FoxmlDirectoryDFSIterator(final File root, final URLFetcher fetcher, final String localFedoraServer,
049            final FileFilter fileFilter) {
050        stack = new Stack<List<File>>();
051        current = new ArrayList<File>(Arrays.asList(root.listFiles()));
052        this.fetcher = fetcher;
053        this.localFedoraServer = localFedoraServer;
054        this.fileFilter = fileFilter;
055    }
056
057    /**
058     * foxml directory DFS iterator with three parameters
059     * @param root the root file
060     * @param resolver the resolver
061     * @param fetcher the fetcher
062     * @param localFedoraServer the domain and port for the server that hosted the fedora objects in the format
063     *                          "localhost:8080".
064     * @param fileFilter a FileFilter that defined which files should be included
065     *        in this Iterator.
066     */
067    public FoxmlDirectoryDFSIterator(final File root, final InternalIDResolver resolver, final URLFetcher fetcher,
068                                     final String localFedoraServer, final FileFilter fileFilter) {
069        this(root, fetcher, localFedoraServer, fileFilter);
070        this.resolver = resolver;
071    }
072
073    private boolean advanceToNext() {
074        while (current.size() > 0 || stack.size() > 0) {
075            if (current.isEmpty()) {
076                current = stack.pop();
077            } else {
078                final File first = current.get(0);
079                if (first.isFile()) {
080                    if (this.fileFilter.accept(first)) {
081                        return true;
082                    } else {
083                        // exclude the current file and get the next one...
084                        current.remove(0);
085                        return advanceToNext();
086                    }
087                } else {
088                    final File directory = current.remove(0);
089                    stack.push(current);
090                    current = new ArrayList<File>(Arrays.asList(directory.listFiles()));
091                }
092            }
093        }
094        return false;
095    }
096
097    @Override
098    public boolean hasNext() {
099        return advanceToNext();
100    }
101
102    @Override
103    public FedoraObjectProcessor next() {
104        if (!advanceToNext()) {
105            throw new IllegalStateException();
106        } else {
107            final File currentFile = current.remove(0);
108            try {
109                return new FoxmlInputStreamFedoraObjectProcessor(
110                        currentFile, fetcher, resolver, localFedoraServer);
111            } catch (final XMLStreamException e) {
112                throw new RuntimeException(currentFile.getPath() + " doesn't appear to be an XML file."
113                        + (e.getMessage() != null ? "  (" + e.getMessage() + ")" : ""));
114            } catch (final FileNotFoundException e) {
115                throw new RuntimeException(e);
116            }
117        }
118    }
119
120    @Override
121    public void remove() {
122        throw new UnsupportedOperationException();
123    }
124
125}