33import java .io .*;
44import java .nio .file .*;
55import java .util .*;
6- import java .util .function .*;
76import java .util .regex .*;
87
98import org .basex .io .*;
109import org .basex .query .*;
10+ import org .basex .query .func .*;
11+ import org .basex .query .util .list .*;
1112import org .basex .query .value .*;
13+ import org .basex .query .value .item .*;
1214import org .basex .query .value .seq .*;
15+ import org .basex .query .value .type .*;
16+ import org .basex .query .var .*;
1317import org .basex .util .*;
1418import org .basex .util .list .*;
1519
@@ -29,44 +33,32 @@ public Value eval(final QueryContext qc) throws QueryException, IOException {
2933 final Pattern pttrn = pattern == null ? null :
3034 Pattern .compile (IOFile .regex (pattern , false ), Prop .CASE ? 0 : Pattern .CASE_INSENSITIVE );
3135 final TokenList tl = new TokenList ();
32- list (dir , recursive , pttrn , tl , dir .getNameCount (), qc );
33- return StrSeq .get (tl );
34- }
35-
36- /**
37- * Returns full file paths.
38- * @param recursive recursive flag
39- * @param qc query context
40- * @return file paths
41- * @throws QueryException query exception
42- * @throws IOException I/O exception
43- */
44- Value paths (final boolean recursive , final QueryContext qc ) throws QueryException , IOException {
45- final TokenList tl = new TokenList ();
46- list (toPath (arg (0 ), qc ), recursive , null , tl , -1 , qc );
36+ final FItem recurse = constantFn (recursive ), filter = constantFn (true );
37+ list (dir , recurse , new HofArgs (1 ), pttrn , dir .getNameCount (),
38+ filter , new HofArgs (1 ), tl , Integer .MAX_VALUE , true , qc );
4739 return StrSeq .get (tl );
4840 }
4941
5042 /**
5143 * Collects the subdirectories and files of the specified directory.
5244 * @param root root path
53- * @param recursive recursive flag
45+ * @param recurse subtree predicate
46+ * @param recurseArgs arguments for the subtree predicate
5447 * @param pattern file name pattern; ignored if {@code null}
48+ * @param index index of root path for relative paths; {@code -1} for absolute paths
49+ * @param filter inclusion predicate
50+ * @param filterArgs arguments for the inclusion predicate
5551 * @param list file list
56- * @param index index of root path
52+ * @param depth maximum number of subdirectory levels to descend ({@code 0}: none)
53+ * @param top {@code true} on the initial call (errors will be propagated)
5754 * @param qc query context
55+ * @throws QueryException query exception
5856 * @throws IOException I/O exception
5957 */
60- private static void list (final Path root , final boolean recursive , final Pattern pattern ,
61- final TokenList list , final int index , final QueryContext qc ) throws IOException {
62-
63- // filter function for adding results
64- final BiConsumer <Path , Boolean > add = (child , dir ) -> {
65- if (pattern == null || pattern .matcher (child .getFileName ().toString ()).matches ()) {
66- final Path path = index < 0 ? child : child .subpath (index , child .getNameCount ());
67- list .add (get (path , dir ).string ());
68- }
69- };
58+ final void list (final Path root , final FItem recurse , final HofArgs recurseArgs ,
59+ final Pattern pattern , final int index , final FItem filter , final HofArgs filterArgs ,
60+ final TokenList list , final int depth , final boolean top , final QueryContext qc )
61+ throws QueryException , IOException {
7062
7163 // collect directories and files first (reduces number of open directory streams)
7264 final ArrayList <Path > dirs = new ArrayList <>(), files = new ArrayList <>();
@@ -77,24 +69,73 @@ private static void list(final Path root, final boolean recursive, final Pattern
7769 }
7870 } catch (final IOException ex ) {
7971 // skip entries that cannot be accessed; throw exception only on root level
80- if (index == - 1 || index == root . getNameCount () ) {
72+ if (top ) {
8173 Util .debug (ex );
8274 throw ex ;
8375 }
76+ return ;
8477 }
8578
8679 // add directories
87- for (final Path dir : dirs ) {
88- add .accept (dir , true );
89- // recursive traversal: do not follow links
90- if (recursive && !Files .isSymbolicLink (dir )) {
91- list (dir , true , pattern , list , index == -1 ? -2 : index , qc );
80+ for (final Path child : dirs ) {
81+ final Str path = add (child , true , pattern , filter , filterArgs , index , list , qc );
82+ // recursive traversal: descend if the depth allows it, do not follow links
83+ if (depth > 0 && !Files .isSymbolicLink (child )) {
84+ final Str p = path != null ? path : get (subPath (child , index ), true );
85+ if (test (recurse , recurseArgs .set (0 , p ), qc )) {
86+ list (child , recurse , recurseArgs , pattern , index , filter , filterArgs ,
87+ list , depth - 1 , false , qc );
88+ }
9289 }
9390 }
9491
9592 // add files
96- for (final Path file : files ) {
97- add . accept ( file , false );
93+ for (final Path child : files ) {
94+ add ( child , false , pattern , filter , filterArgs , index , list , qc );
9895 }
9996 }
97+
98+ /**
99+ * Adds an entry to the result list, applying pattern and filter checks.
100+ * @param child raw path
101+ * @param isDir directory flag
102+ * @param pattern file name pattern (can be {@code null})
103+ * @param filter inclusion predicate
104+ * @param filterArgs arguments for the inclusion predicate
105+ * @param index index of root path (or {@code -1})
106+ * @param list file list
107+ * @param qc query context
108+ * @return display path, or {@code null} if the pattern excluded the entry
109+ * @throws QueryException query exception
110+ */
111+ private Str add (final Path child , final boolean isDir , final Pattern pattern ,
112+ final FItem filter , final HofArgs filterArgs , final int index , final TokenList list ,
113+ final QueryContext qc ) throws QueryException {
114+ // pattern check (operates on the file name)
115+ if (pattern != null && !pattern .matcher (child .getFileName ().toString ()).matches ()) return null ;
116+
117+ final Str path = get (subPath (child , index ), isDir );
118+ if (test (filter , filterArgs .set (0 , path ), qc )) list .add (path .string ());
119+ return path ;
120+ }
121+
122+ /**
123+ * Returns the effective result path.
124+ * @param child raw path
125+ * @param index index of root path for relative paths; {@code -1} for absolute paths
126+ * @return display path
127+ */
128+ private static Path subPath (final Path child , final int index ) {
129+ return index < 0 ? child : child .subpath (index , child .getNameCount ());
130+ }
131+
132+ /**
133+ * Creates a boolean predicate function.
134+ * @param value return value
135+ * @return function item
136+ */
137+ final FItem constantFn (final boolean value ) {
138+ return new FuncItem (info , Bln .get (value ), new Var [0 ], AnnList .EMPTY ,
139+ FuncType .get (Types .BOOLEAN_O ), 0 , null );
140+ }
100141}
0 commit comments