Implementación de lista enlazada de directorios FileSystem
Estoy escribiendo clases contenedoras en Java que anulan los métodos de una implementación existente para manejar un caso extremo. La implementación completa es un poco más compleja de lo que debe publicarse aquí, por lo que he escrito una clase simplificada que contiene solo las partes para las que solicito asistencia.
Resumen del problema
Estoy extendiendo dos clases:
Una clase diseñada como una clase de "enumeración", que abstrae un directorio en un sistema de archivos que contiene enlaces simbólicos a otros directorios. (Mundo real: "/sys/block".). Tiene dos métodos, un scan()
método para generar la lista de subdirectorios (vinculados) y otro getFirst()
para devolver el primer elemento de la lista.
La segunda clase es una clase de "entrada", que abstrae el directorio apuntado enumerado por la primera clase. Tiene dos métodos, un getName()
método para devolver la ruta del directorio como una cadena y un getNext()
método para iterar al siguiente elemento.
Restricciones
- Compatibilidad con JDK 8 o anterior
- Se puede suponer el uso de un solo subproceso
- Los constructores pueden modificarse según sea necesario.
- Debe implementar (al menos) las dos clases especificadas y los dos métodos en cada una.
Enfoque de revisión
El scan()
método es mi lucha aquí. Creo que puedo haber complicado demasiado la solución de dos maneras:
try ... catch
Los bloques anidados en elscan()
método parecen inusuales. ¿Me estoy perdiendo una forma más sencilla de manejar esto?- (ACTUALIZACIÓN: Respondí esta segunda pregunta, a continuación). El patrón implementado es obviamente una lista vinculada individualmente en la que estoy trabajando al pasar una
ArrayList
implementación. Puedo imaginar que laDirEntry
clase contenga solo su objetoPath
y unDirEntry next
objeto, pero los intentos de generar dicha lista parecen aún más complejos o menos eficaces que la solución que he creado.
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class DeviceList {
/**
* Class representing a parent directory which contains symbolic links to other
* directories
*/
static class DirEnumerator {
private Path dirPath;
private List<DirEntry> entryList = Collections.emptyList();
public DirEnumerator(String path) {
dirPath = FileSystems.getDefault().getPath(path);
}
/**
* Scans the directory for entries
*
* @return The number of entries found
*/
public int scan() {
try (Stream<Path> paths = Files.walk(dirPath)) {
List<Path> linkedDirs = paths.filter(Files::isSymbolicLink).map(p -> {
try {
return Files.readSymbolicLink(p);
} catch (IOException e) {
return p;
}
}).collect(Collectors.toList());
this.entryList = new ArrayList<>();
for (int i = 0; i < linkedDirs.size(); i++) {
this.entryList.add(new DirEntry(entryList, linkedDirs.get(i), i));
}
return this.entryList.size();
} catch (IOException e) {
this.entryList = Collections.emptyList();
return 0;
}
}
/**
* Gets the first entry in the scanned list
*
* @return The first entry if it exists; null otherwise
*/
public DirEntry getFirst() {
return entryList.isEmpty() ? null : entryList.get(0);
}
}
/**
* Class representing a directory
*/
static class DirEntry {
private List<DirEntry> entryList;
private Path path;
private int index;
public DirEntry(List<DirEntry> entryList, Path path, int i) {
this.entryList = entryList;
this.path = path;
this.index = i;
}
/**
* Gets the path name of the directory entry
*
* @return a string representing the path
*/
public String getName() {
return this.path.toString();
}
/**
* Gets the next entry in the list
*
* @return the next entry if it exists; null otherwise
*/
public DirEntry getNext() {
int nextIndex = index + 1;
return nextIndex < entryList.size() ? entryList.get(nextIndex) : null;
}
}
public static void main(String[] args) {
// Test on any directory containing symbolic links to other directories
DirEnumerator de = new DirEnumerator("/sys/block");
int n = de.scan();
System.out.println("Found " + n + " directories.");
DirEntry e = de.getFirst();
while (e != null) {
System.out.println("Directory: " + e.getName());
e = e.getNext();
}
}
}
```
Respuestas
Descubrí una forma más sencilla de hacer la segunda pregunta, creando la Lista vinculada iterando hacia atrás desde las rutas generadas.
static class DirEnumerator {
private Path dirPath;
private DirEntry first = null;
// ...
public int scan() {
try (Stream<Path> paths = Files.walk(dirPath)) {
List<Path> linkedDirs = paths.filter(Files::isSymbolicLink).map(p -> {
try {
return Files.readSymbolicLink(p);
} catch (IOException e) {
return p;
}
}).collect(Collectors.toList());
this.first = null;
int i = linkedDirs.size();
while (i-- > 0) {
this.first = new DirEntry(linkedDirs.get(i), first);
}
return linkedDirs.size();
} catch (IOException e) {
this.first = null;
return 0;
}
}
// ...
}
static class DirEntry {
private Path path;
private DirEntry next;
public DirEntry(Path path, DirEntry next) {
this.path = path;
this.next = next;
}
// ...
}
```