/**
  * Returns an array of strings naming the files and directories in the directory denoted by this
  * abstract pathname, and all of its subdirectories.
  *
  * <p>If this abstract pathname does not denote a directory, then this method returns {@code
  * null}. Otherwise an array of strings is returned, one for each file or directory in the
  * directory and its subdirectories. Names denoting the directory itself and the directory's
  * parent directory are not included in the result. Each string is a path relative to the given
  * directory.
  *
  * <p>There is no guarantee that the name strings in the resulting array will appear in any
  * specific order; they are not, in particular, guaranteed to appear in alphabetical order.
  *
  * @param path the abstract pathname to list
  * @return An array of strings naming the files and directories in the directory denoted by this
  *     abstract pathname and its subdirectories. The array will be empty if the directory is
  *     empty. Returns {@code null} if this abstract pathname does not denote a directory.
  * @throws IOException if a non-Alluxio error occurs
  */
 public String[] listRecursive(String path) throws IOException {
   // Clean the path by creating a URI and turning it back to a string
   AlluxioURI uri = new AlluxioURI(path);
   path = uri.toString();
   List<String> returnPaths = new ArrayList<>();
   Queue<String> pathsToProcess = new ArrayDeque<>();
   // We call list initially, so we can return null if the path doesn't denote a directory
   String[] subpaths = list(path);
   if (subpaths == null) {
     return null;
   } else {
     for (String subp : subpaths) {
       pathsToProcess.add(PathUtils.concatPath(path, subp));
     }
   }
   while (!pathsToProcess.isEmpty()) {
     String p = pathsToProcess.remove();
     returnPaths.add(p.substring(path.length() + 1));
     // Add all of its subpaths
     subpaths = list(p);
     if (subpaths != null) {
       for (String subp : subpaths) {
         pathsToProcess.add(PathUtils.concatPath(p, subp));
       }
     }
   }
   return returnPaths.toArray(new String[returnPaths.size()]);
 }