Skip to content

Commit

Permalink
Merge pull request #5093 from line-o/backport/5028
Browse files Browse the repository at this point in the history
[6.x.x] Handle IllegalStateException in file:directory-list#2
  • Loading branch information
adamretter authored Oct 19, 2023
2 parents ec00524 + 8ad2fd5 commit a1ba17f
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;

import org.apache.logging.log4j.LogManager;
Expand All @@ -34,11 +33,7 @@
import org.exist.dom.QName;
import org.exist.dom.memtree.MemTreeBuilder;
import org.exist.util.FileUtils;
import org.exist.xquery.BasicFunction;
import org.exist.xquery.Cardinality;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.*;
import org.exist.xquery.value.DateTimeValue;
import org.exist.xquery.value.FunctionParameterSequenceType;
import org.exist.xquery.value.FunctionReturnSequenceType;
Expand All @@ -47,50 +42,56 @@
import org.exist.xquery.value.SequenceType;
import org.exist.xquery.value.Type;

import static org.exist.xquery.modules.file.FileErrorCode.DIRECTORY_NOT_FOUND;

/**
* eXist File Module Extension DirectoryList
*
* <p>
* Enumerate a list of files, including their size and modification time, found
* in a specified directory, using a pattern
*
* @author <a href="mailto:[email protected]">Andrzej Taramina</a>
* @author ljo
* @serial 2009-08-09
* @version 1.2
*
* @see
* org.exist.xquery.BasicFunction#BasicFunction(org.exist.xquery.XQueryContext,
* @serial 2009-08-09
* @see org.exist.xquery.BasicFunction#BasicFunction(org.exist.xquery.XQueryContext,
* org.exist.xquery.FunctionSignature)
*/
public class DirectoryList extends BasicFunction {

private final static Logger logger = LogManager.getLogger(DirectoryList.class);

final static String NAMESPACE_URI = FileModule.NAMESPACE_URI;
final static String PREFIX = FileModule.PREFIX;

public final static FunctionSignature[] signatures
= {
new FunctionSignature(
new QName("directory-list", NAMESPACE_URI, PREFIX),
"List all files, including their file size and modification time, "
+ "found in or below a directory, $directory. Files are located in the server's "
+ "file system, using filename patterns, $pattern. File pattern matching is based "
+ "on code from Apache's Ant, thus following the same conventions. For example:\n\n"
+ "'*.xml' matches any file ending with .xml in the current directory,\n- '**/*.xml' matches files "
+ "in any directory below the specified directory. This method is only available to the DBA role.",
new SequenceType[]{
static final String NAMESPACE_URI = FileModule.NAMESPACE_URI;
static final String PREFIX = FileModule.PREFIX;
public static final FunctionSignature[] signatures = {
new FunctionSignature(
new QName("directory-list", NAMESPACE_URI, PREFIX),
"List all files, including their file size and modification time, "
+ "found in or below a directory, $directory. Files are located in the server's "
+ "file system, using filename patterns, $pattern. File pattern matching is based "
+ "on code from Apache's Ant, thus following the same conventions. For example:\n\n"
+ "'*.xml' matches any file ending with .xml in the current directory,\n- '**/*.xml' matches files "
+ "in any directory below the specified directory. This method is only available to the DBA role.",
new SequenceType[]{
new FunctionParameterSequenceType("path", Type.ITEM,
Cardinality.EXACTLY_ONE, "The base directory path or URI in the file system where the files are located."),
new FunctionParameterSequenceType("pattern", Type.STRING,
Cardinality.ZERO_OR_MORE, "The file name pattern")
},
new FunctionReturnSequenceType(Type.NODE,
Cardinality.ZERO_OR_ONE, "a node fragment that shows all matching "
+ "filenames, including their file size and modification time, and "
+ "the subdirectory they were found in")
)
};
},
new FunctionReturnSequenceType(Type.NODE,
Cardinality.ZERO_OR_ONE, "a node fragment that shows all matching "
+ "filenames, including their file size and modification time, and "
+ "the subdirectory they were found in")
)
};
static final QName FILE_ELEMENT = new QName("file", NAMESPACE_URI, PREFIX);
static final QName LIST_ELEMENT = new QName("list", NAMESPACE_URI, PREFIX);

static final QName DIRECTORY_ATTRIBUTE = new QName("directory", null, null);
static final QName NAME_ATTRIBUTE = new QName("name", null, null);
static final QName SIZE_ATTRIBUTE = new QName("size", null, null);
static final QName HUMAN_SIZE_ATTRIBUTE = new QName("human-size", null, null);
static final QName MODIFIED_ATTRIBUTE = new QName("modified", null, null);
static final QName SUBDIR_ATTRIBUTE = new QName("subdir", null, null);
private static final Logger logger = LogManager.getLogger(DirectoryList.class);

public DirectoryList(final XQueryContext context, final FunctionSignature signature) {
super(context, signature);
Expand All @@ -117,11 +118,11 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
final MemTreeBuilder builder = context.getDocumentBuilder();

builder.startDocument();
builder.startElement(new QName("list", NAMESPACE_URI, PREFIX), null);
builder.addAttribute(new QName("directory", null, null), baseDir.toString());
builder.startElement(LIST_ELEMENT, null);
builder.addAttribute(DIRECTORY_ATTRIBUTE, baseDir.toString());
try {
final int patternsLen = patterns.getItemCount();
final String includes[] = new String[patternsLen];
final String[] includes = new String[patternsLen];
for (int i = 0; i < patternsLen; i++) {
includes[i] = patterns.itemAt(0).getStringValue();
}
Expand All @@ -139,76 +140,52 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
logger.debug("Found: {}", file.toAbsolutePath());
}

String relPath = file.toString().substring(baseDir.toString().length() + 1);
final String relPath = file.toString().substring(baseDir.toString().length() + 1);

int lastSeparatorPosition = relPath.lastIndexOf(java.io.File.separatorChar);
builder.startElement(FILE_ELEMENT, null);
builder.addAttribute(NAME_ATTRIBUTE, FileUtils.fileName(file));

String relDir = null;
if (lastSeparatorPosition >= 0) {
relDir = relPath.substring(0, lastSeparatorPosition);
relDir = relDir.replace(java.io.File.separatorChar, '/');
}

builder.startElement(new QName("file", NAMESPACE_URI, PREFIX), null);

builder.addAttribute(new QName("name", null, null), FileUtils.fileName(file));
final long sizeLong = FileUtils.sizeQuietly(file);
builder.addAttribute(SIZE_ATTRIBUTE, Long.toString(sizeLong));
builder.addAttribute(HUMAN_SIZE_ATTRIBUTE, getHumanSize(sizeLong));

Long sizeLong = FileUtils.sizeQuietly(file);
String sizeString = Long.toString(sizeLong);
String humanSize = getHumanSize(sizeLong, sizeString);
builder.addAttribute(MODIFIED_ATTRIBUTE,
new DateTimeValue(this,
new Date(Files.getLastModifiedTime(file).toMillis())).getStringValue());

builder.addAttribute(new QName("size", null, null), sizeString);
builder.addAttribute(new QName("human-size", null, null), humanSize);
builder.addAttribute(new QName("modified", null, null), new DateTimeValue(this, new Date(Files.getLastModifiedTime(file).toMillis())).getStringValue());

if (relDir != null && !relDir.isEmpty()) {
builder.addAttribute(new QName("subdir", null, null), relDir);
final int lastSeparatorPosition = relPath.lastIndexOf(java.io.File.separatorChar);
if (lastSeparatorPosition >= 0) {
final String relDir = relPath.substring(0, lastSeparatorPosition);
if (!relDir.isEmpty()) {
builder.addAttribute(SUBDIR_ATTRIBUTE,
relDir.replace(java.io.File.separatorChar, '/'));
}
}

builder.endElement();

}

builder.endElement();

return (NodeValue) builder.getDocument().getDocumentElement();
} catch (final IOException e) {
throw new XPathException(this, e.getMessage());
} catch (final IOException | IllegalStateException e) {
throw new XPathException(this, DIRECTORY_NOT_FOUND, e.getMessage());
} finally {
context.popDocumentContext();
}
}

private String getHumanSize(final Long sizeLong, final String sizeString) {
String humanSize = "n/a";
int sizeDigits = sizeString.length();

if (sizeDigits < 4) {
humanSize = Long.toString(Math.abs(sizeLong));

} else if (sizeDigits >= 4 && sizeDigits <= 6) {
if (sizeLong < 1024) {
// We don't want 0KB för e.g. 1006 Bytes.
humanSize = Long.toString(Math.abs(sizeLong));
} else {
humanSize = Math.abs(sizeLong / 1024) + "KB";
}

} else if (sizeDigits >= 7 && sizeDigits <= 9) {
if (sizeLong < 1048576) {
humanSize = Math.abs(sizeLong / 1024) + "KB";
} else {
humanSize = Math.abs(sizeLong / (1024 * 1024)) + "MB";
}

} else if (sizeDigits > 9) {
if (sizeLong < 1073741824) {
humanSize = Math.abs((sizeLong / (1024 * 1024))) + "MB";
} else {
humanSize = Math.abs((sizeLong / (1024 * 1024 * 1024))) + "GB";
}
private String getHumanSize(final Long sizeLong) {
if (sizeLong < 1024) {
return Math.abs(sizeLong) + "B";
}
if (sizeLong < 1048576) {
return Math.abs(sizeLong / 1024) + "KB";
}
if (sizeLong < 1073741824) {
return Math.abs((sizeLong / (1024 * 1024))) + "MB";
}
return humanSize;
return Math.abs((sizeLong / (1024 * 1024 * 1024))) + "GB";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* [email protected]
* http://www.exist-db.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.xquery.modules.file;

import org.exist.dom.QName;
import org.exist.xquery.ErrorCodes;

class FileErrorCode extends ErrorCodes.ErrorCode {
public static final ErrorCodes.ErrorCode DIRECTORY_NOT_FOUND = new FileErrorCode("DIRECTORY_NOT_FOUND",
"The directory could not be found.");

FileErrorCode(final String code, final String description) {
super(new QName(code, FileModule.NAMESPACE_URI, FileModule.PREFIX), description);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
(:
: eXist-db Open Source Native XML Database
: Copyright (C) 2001 The eXist-db Authors
:
: [email protected]
: http://www.exist-db.org
:
: This library is free software; you can redistribute it and/or
: modify it under the terms of the GNU Lesser General Public
: License as published by the Free Software Foundation; either
: version 2.1 of the License, or (at your option) any later version.
:
: This library is distributed in the hope that it will be useful,
: but WITHOUT ANY WARRANTY; without even the implied warranty of
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
: Lesser General Public License for more details.
:
: You should have received a copy of the GNU Lesser General Public
: License along with this library; if not, write to the Free Software
: Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
:)
xquery version "3.1";

module namespace dirlist="http://exist-db.org/testsuite/modules/file/dirlist";


import module namespace file="http://exist-db.org/xquery/file";


declare namespace test="http://exist-db.org/xquery/xqsuite";


declare
%test:assertError("file:DIRECTORY_NOT_FOUND")
function dirlist:non-existent-error-code() {
file:directory-list("/non/existent", ())
};

declare
%test:assertXPath("contains($result,'basedir /non/existent does not exist.') or contains($result,'basedir \non\existent does not exist.')")
function dirlist:non-existent-error-description() {
try {
file:directory-list("/non/existent", ())
} catch file:DIRECTORY_NOT_FOUND {
$err:description
}
};

0 comments on commit a1ba17f

Please sign in to comment.