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