/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE file at the root of the source
* tree and available online at
*
* https://github.com/keeps/roda
*/
package org.roda.core.storage.utils;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Iterator;
import org.apache.commons.io.IOUtils;
import org.roda.core.common.iterables.CloseableIterable;
import org.roda.core.data.exceptions.AuthorizationDeniedException;
import org.roda.core.data.exceptions.GenericException;
import org.roda.core.data.exceptions.NotFoundException;
import org.roda.core.data.exceptions.RequestNotValidException;
import org.roda.core.data.v2.ip.StoragePath;
import org.roda.core.storage.Resource;
import org.roda.core.storage.StorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StorageRecursiveListingUtils {
private StorageRecursiveListingUtils() {
}
private static final Logger LOGGER = LoggerFactory.getLogger(StorageRecursiveListingUtils.class);
private static CloseableIterable<Resource> recursiveListing(final StorageService storage,
final CloseableIterable<Resource> iterable) {
final Iterator<Resource> directlyUnder = iterable.iterator();
return new CloseableIterable<Resource>() {
@Override
public Iterator<Resource> iterator() {
return new Iterator<Resource>() {
ArrayDeque<CloseableIterable<Resource>> itStack = new ArrayDeque<>();
@Override
public boolean hasNext() {
boolean hasNext;
if (itStack.isEmpty()) {
hasNext = directlyUnder.hasNext();
} else {
hasNext = false;
// find a non-empty iterator or empty stack
do {
if (!itStack.peek().iterator().hasNext()) {
try {
itStack.pop().close();
} catch (IOException e) {
LOGGER.warn("Error closing file iterable, possible file leak", e);
}
} else {
hasNext = true;
}
} while (!hasNext && !itStack.isEmpty());
if (itStack.isEmpty()) {
hasNext = directlyUnder.hasNext();
}
}
return hasNext;
}
@Override
public Resource next() {
Resource resource;
if (itStack.isEmpty()) {
resource = directlyUnder.next();
} else {
resource = itStack.peek().iterator().next();
}
try {
if (resource != null && resource.isDirectory()) {
CloseableIterable<Resource> subIterable = storage.listResourcesUnderDirectory(resource.getStoragePath(),
false);
itStack.push(subIterable);
}
} catch (RequestNotValidException | GenericException | NotFoundException | AuthorizationDeniedException e) {
LOGGER.warn("Error while listing all files", e);
}
return resource;
}
};
}
@Override
public void close() throws IOException {
iterable.close();
}
};
}
/**
* Emulates recursive listing using non-recursive listing
*
* @param storage
* @param storagePath
* @return
* @throws NotFoundException
* @throws GenericException
* @throws RequestNotValidException
* @throws AuthorizationDeniedException
*/
public static CloseableIterable<Resource> listAllUnderDirectory(final StorageService storage,
final StoragePath storagePath)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
final CloseableIterable<Resource> iterable = storage.listResourcesUnderDirectory(storagePath, false);
return recursiveListing(storage, iterable);
}
/**
* Emulates recursive listing using non-recursive listing
*
* @param storage
* @param storagePath
* @return
* @throws NotFoundException
* @throws GenericException
* @throws RequestNotValidException
* @throws AuthorizationDeniedException
*/
public static CloseableIterable<Resource> listAllUnderContainer(StorageService storage, StoragePath storagePath)
throws NotFoundException, GenericException, AuthorizationDeniedException, RequestNotValidException {
final CloseableIterable<Resource> iterable = storage.listResourcesUnderContainer(storagePath, false);
return recursiveListing(storage, iterable);
}
public static Long countAllUnderDirectory(StorageService storage, StoragePath storagePath)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
Long ret = 0L;
CloseableIterable<Resource> iterable = listAllUnderDirectory(storage, storagePath);
Iterator<Resource> it = iterable.iterator();
while (it.hasNext()) {
ret++;
}
IOUtils.closeQuietly(iterable);
return ret;
}
public static Long countAllUnderContainer(StorageService storage, StoragePath storagePath)
throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException {
Long ret = 0L;
CloseableIterable<Resource> iterable = listAllUnderContainer(storage, storagePath);
Iterator<Resource> it = iterable.iterator();
while (it.hasNext()) {
ret++;
}
IOUtils.closeQuietly(iterable);
return ret;
}
}