/*
* $URL:https://secure.revolsys.com/svn/open.revolsys.com/GIS/trunk/src/main/java/com/revolsys/gis/format/core/io/AbstractDirectoryReader.java $
* $Author:paul.austin@revolsys.com $
* $Date:2007-06-09 09:28:28 -0700 (Sat, 09 Jun 2007) $
* $Revision:265 $
* Copyright 2004-2005 Revolution Systems Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.revolsys.io;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import javax.annotation.PostConstruct;
import org.apache.log4j.Logger;
import com.revolsys.io.filter.ExtensionFilenameFilter;
import com.revolsys.spring.resource.FileSystemResource;
import com.revolsys.spring.resource.Resource;
public abstract class AbstractDirectoryReader<T> extends AbstractReader<T> implements Iterator<T> {
/** The list of base file names to read. */
private final List<String> baseFileNames = new ArrayList<>();
/** The current directory being processed. */
private File currentFile;
/** The current iterator. */
private Iterator<T> currentIterator;
/** The reader for the current directory being processed. */
private Reader<T> currentReader;
/** The directory of data to read from. */
private File directory;
/** The filter used to select files from the directory. */
private FilenameFilter fileNameFilter;
/** The files to be read by this reader. */
private List<File> files;
/** Flag indicating if the reader has more objects to be read. */
private boolean hasNext = true;
/** The logging instance. */
private final Logger log = Logger.getLogger(getClass());
/** The files to be read by this reader. */
private Iterator<Entry<File, Reader<T>>> readerIterator;
private final Map<File, Reader<T>> readers = new LinkedHashMap<>();
/**
* Construct a new AbstractDirectoryReader.
*/
public AbstractDirectoryReader() {
}
/**
* Close the reader.
*/
@Override
public void close() {
if (this.currentReader != null) {
this.currentReader.close();
}
}
/**
* Get the list of base file names to read.
*
* @return The list of base file names to read.
*/
public List<String> getBaseFileNames() {
return this.baseFileNames;
}
/**
* Get the directory containing the files to read.
*
* @return The directory containing the files to read.
*/
public File getDirectory() {
return this.directory;
}
/**
* Get the filter used to select files from the directory.
*
* @return The filter used to select files from the directory.
*/
public FilenameFilter getFileNameFilter() {
return this.fileNameFilter;
}
/**
* Get the files that are to be read by this reader. This must be overwritten
* in sub classes to return the files in the working directory that are to be
* read by instances of the write returned by {@link #newReader(File)}.
*
* @return The list of files.
*/
protected List<File> getFiles() {
final File[] files;
if (this.fileNameFilter == null) {
files = this.directory.listFiles();
} else {
files = this.directory.listFiles(this.fileNameFilter);
}
List<File> fileList;
if (this.baseFileNames.isEmpty()) {
Arrays.sort(files);
fileList = Arrays.asList(files);
} else {
fileList = new ArrayList<>();
final Map<String, File> fileBaseNameMap = new HashMap<>();
for (final File file : files) {
final String baseName = FileUtil.getBaseName(file).toUpperCase();
fileBaseNameMap.put(baseName, file);
}
for (final String baseName : this.baseFileNames) {
final File file = fileBaseNameMap.get(baseName);
if (file != null) {
fileList.add(file);
}
}
}
return fileList;
}
/**
* Check to see if the reader has more data objects to be read.
*
* @return True if the reader has more data objects to be read.
*/
@Override
public boolean hasNext() {
while (this.hasNext && (this.currentIterator == null || !this.currentIterator.hasNext())) {
if (this.readerIterator.hasNext()) {
if (this.currentReader != null) {
try {
this.currentReader.close();
} catch (final Throwable t) {
this.log.warn(t.getMessage(), t);
}
}
final Entry<File, Reader<T>> entry = this.readerIterator.next();
this.currentFile = entry.getKey();
try {
this.currentReader = entry.getValue();
this.currentIterator = this.currentReader.iterator();
this.hasNext = this.currentIterator.hasNext();
} catch (final Throwable e) {
this.hasNext = false;
this.log.error(e.getMessage(), e);
}
} else {
this.hasNext = false;
}
}
return this.hasNext;
}
/**
* Get the iterator.
*
* @return The iterator.
*/
@Override
public Iterator<T> iterator() {
return this;
}
/**
* Construct a new new {@link Reader} to read the file.
*
* @param file The file to read.
* @return The reader for the file.
*/
protected abstract Reader<T> newReader(Resource file);
/**
* Get the next data object read by this reader.
*
* @return The next Record.
* @exception NoSuchElementException If the reader has no more data objects.
*/
@Override
public T next() {
if (hasNext()) {
final T record = this.currentIterator.next();
return record;
} else {
throw new NoSuchElementException();
}
}
@Override
@PostConstruct
public void open() {
for (final File file : getFiles()) {
final FileSystemResource resource = new FileSystemResource(file);
final Reader<T> reader = newReader(resource);
reader.open();
if (reader != null) {
this.readers.put(file, reader);
}
}
this.readerIterator = this.readers.entrySet().iterator();
hasNext();
}
/**
* Removing data objects is not supported.
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
/**
* Set the list of base file names to read.
*
* @param baseFileNames The list of base file names to read.
*/
public void setBaseFileNames(final Collection<String> baseFileNames) {
this.baseFileNames.clear();
for (final String name : baseFileNames) {
this.baseFileNames.add(name.toUpperCase());
}
}
/**
* Set the directory containing the files to read.
*
* @param directory The directory containing the files to read.
*/
public void setDirectory(final File directory) {
if (!directory.isDirectory()) {
throw new IllegalArgumentException("File must exist and be a directory " + directory);
} else {
this.directory = directory;
this.files = getFiles();
}
}
public void setFileExtensions(final Collection<String> fileExtensions) {
this.fileNameFilter = new ExtensionFilenameFilter(fileExtensions);
}
public void setFileExtensions(final String... fileExtensions) {
setFileExtensions(Arrays.asList(fileExtensions));
}
/**
* Set the filter used to select files from the directory.
*
* @param fileNameFilter The filter used to select files from the directory.
*/
public void setFileNameFilter(final FilenameFilter fileNameFilter) {
this.fileNameFilter = fileNameFilter;
}
}