/**
* Copyright 2015 StreamSets Inc.
*
* Licensed under the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.streamsets.pipeline.lib.io;
import com.streamsets.pipeline.api.impl.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;
public class GlobDirectoryStream implements DirectoryStream<Path> {
private static final Logger LOG = LoggerFactory.getLogger(GlobDirectoryStream.class);
private final AtomicInteger openCounter;
private final DirectoryStream<Path> directoryStream;
private final Iterator<Path> iterator;
public GlobDirectoryStream(Path basePath, Path globPath) throws IOException {
this(basePath, globPath, new Filter<Path>() {
@Override
public boolean accept(Path entry) throws IOException {
return true;
}
}, new AtomicInteger());
}
public GlobDirectoryStream(Path basePath, Path globPath, DirectoryStream.Filter<Path> filter) throws IOException {
this(basePath, globPath, filter, new AtomicInteger());
}
public int getOpenCounter() {
return openCounter.get();
}
GlobDirectoryStream(Path basePath, Path globPath, final DirectoryStream.Filter<Path> filter,
AtomicInteger openCounter) throws IOException {
Utils.checkNotNull(basePath, "basePath");
Utils.checkNotNull(globPath, "globPath");
Utils.checkNotNull(filter, "filter");
LOG.trace("<init>(basePath={}, globPath={}", basePath, globPath);
this.openCounter = openCounter;
if (globPath.getNameCount() == 1) {
final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + globPath.getName(0).toString());
DirectoryStream.Filter<Path> globFilter = new Filter<Path>() {
@Override
public boolean accept(Path entry) throws IOException {
return matcher.matches(entry.getFileName()) && filter.accept(entry);
}
};
directoryStream = Files.newDirectoryStream(basePath, globFilter);
openCounter.incrementAndGet();
iterator = directoryStream.iterator();
} else {
String firstGlob = globPath.getName(0).toString();
final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + firstGlob);
DirectoryStream.Filter<Path> globFilter = new Filter<Path>() {
@Override
public boolean accept(Path entry) throws IOException {
return matcher.matches(entry.getFileName());
}
};
DirectoryStream<Path> baseDirectoryStream = Files.newDirectoryStream(basePath, globFilter);
openCounter.incrementAndGet();
String newGlobPathName = globPath.getName(1).toString();
String[] children = new String[globPath.getNameCount() - 2];
for (int i = 2; i < globPath.getNameCount(); i++) {
children[i - 2] = globPath.getName(i).toString();
}
final Path newGlobPath = Paths.get(newGlobPathName, children);
directoryStream = new GlobDirectoryStream(baseDirectoryStream, newGlobPath, filter, openCounter);
openCounter.incrementAndGet();
iterator = directoryStream.iterator();
}
}
private DirectoryStream<Path> childDirectoryStream = null;
GlobDirectoryStream(final DirectoryStream<Path> directoryStream, final Path globPath,
final DirectoryStream.Filter<Path> filter, final AtomicInteger openCounter) throws IOException {
this.openCounter = openCounter;
this.directoryStream = directoryStream;
final Iterator<Path> dirIterator = directoryStream.iterator();
iterator = new Iterator<Path>() {
Iterator<Path> it = Collections.emptyIterator();
@Override
public boolean hasNext() {
try {
while (!it.hasNext() && dirIterator.hasNext()) {
if (childDirectoryStream != null) {
childDirectoryStream.close();
childDirectoryStream = null;
openCounter.decrementAndGet();
}
Path basePath = dirIterator.next();
if (Files.isDirectory(basePath)) {
childDirectoryStream = new GlobDirectoryStream(basePath, globPath, filter, openCounter);
openCounter.incrementAndGet();
it = childDirectoryStream.iterator();
}
}
boolean hasNext = it.hasNext();
if (!hasNext) {
if (childDirectoryStream != null) {
childDirectoryStream.close();
childDirectoryStream = null;
openCounter.decrementAndGet();
}
}
return hasNext;
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
@Override
public Path next() {
Utils.checkState(hasNext(), "Iterator does not have more elements");
return it.next();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public Iterator<Path> iterator() {
return iterator;
}
@Override
public void close() throws IOException {
try {
if (childDirectoryStream != null) {
childDirectoryStream.close();
childDirectoryStream = null;
openCounter.decrementAndGet();
}
} finally {
directoryStream.close();
openCounter.decrementAndGet();
}
}
}