summaryrefslogtreecommitdiff
path: root/support/texosquery/java/FileWalkVisitor.java
blob: 89e4fe738159a980453c742ec502cf970e99394c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
/*
    Copyright (C) 2016 Nicola L.C. Talbot
    www.dickimaw-books.com

    This work may be distributed and/or modified under the
    conditions of the LaTeX Project Public License, either version 1.3
    of this license or (at your option) any later version.
    The latest version of this license is in
    http://www.latex-project.org/lppl.txt
    and version 1.3 or later is part of all distributions of LaTeX
    version 2005/12/01 or later.
*/
package com.dickimawbooks.texosquery;

import java.io.File;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.nio.file.Files;
import java.nio.file.FileVisitResult;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Vector;
import java.util.Comparator;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

/**
 * This class is used during a file walk to determine if a directory
 * may be descended and to add allowed files to the list.
 * This relies on the java.nio.file library, which was introduced to
 * Java 7, so this isn't available for the JRE5 version.
 * @since 1.2
 */
public class FileWalkVisitor extends SimpleFileVisitor<Path>
{
   /**
    * Creates a new visitor for a file walk. This will only allow
    * regular, non-hidden, readable files, where the name matches
    * the supplied regular expression. The found files are inserted
    * into the results list according to the comparator.
    */ 
   public FileWalkVisitor(TeXOSQuery invoker, String regex, 
     Comparator<Path> comparator)
   {
      this.invoker = invoker;
      this.comparator = comparator;
      pattern = Pattern.compile(regex);
      result = new Vector<Path>();
   }

   /**
    * Returns the list built during the file walk. 
    * @return file list
    */ 
   public Vector<Path> getList()
   {
      return result;
   }

   /**
    * Invoked for a directory before entries in the directory are
    * visited. 
    *
    * Hidden directories are automatically skipped, even if
    * openin_any is <tt>a</tt>. Symbolic links and directories without 
    * read access are also skipped.
    *
    * @param dir the directory about to be visited
    * @param attrs the directory's basic attributes
    * @return the visit result
    */ 
   @Override
   public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
    throws IOException
   {
      if (Files.isHidden(dir) || !Files.isReadable(dir)
        || Files.isSymbolicLink(dir))
      {
         invoker.info(String.format(
           "Walk skipping directory: %s", dir.toString()));
         return FileVisitResult.SKIP_SUBTREE;
      }

      // We don't need to check openin_any here as the starting
      // directory of the walk should already have been checked to
      // ensure it's on the CWD path.

      return FileVisitResult.CONTINUE;
   }


   /**
    * Invoked for a file in a directory. 
    *
    * Hidden files are automatically skipped, regardless of
    * openin_any. Files that don't have read access are also skipped
    * and so are symbolic files.
    *
    * @param file a reference to the file
    * @param attrs the file's basic attributes
    * @return the visit result
    */ 
   @Override
   public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
    throws IOException
   {
      if (Files.isHidden(file) || !Files.isReadable(file)
          || attrs.isSymbolicLink())
      {
         invoker.info(String.format(
           "Walk skipping file: %s", file.toString()));
         return FileVisitResult.CONTINUE;
      }

      // Does the file name match the supplied pattern?

      String name = file.getName(file.getNameCount()-1).toString();

      Matcher m = pattern.matcher(name);

      if (m.matches())
      {
         addPath(file);
      }

      return FileVisitResult.CONTINUE;
   }

   /**
    * Inserts path into results list according to the comparator. 
    * @param path the path to be added to the list
    */ 
   private void addPath(Path path)
   {
      int n = result.size();

      if (n == 0)
      {
         result.add(path);
         return;
      }

      for (int i = 0; i < n; i++)
      {
         Path thisPath = result.get(i);

         if (comparator.compare(thisPath, path) > 0)
         {
            result.add(i, path);
            return;
         }
      }

      result.add(path);
   }

   /**
    *Walks the path starting from the given directory, which must be the 
    *current working directory or a descendent. 
    */ 
   public static String walk(TeXOSQuery invoker, 
        String separator,
        String regex, 
        File directory,
        FileSortType sortType)
   throws IOException
   {
      if (!directory.exists())
      {
         throw new FileNotFoundException(
          String.format("No such file: %s", directory.toString()));
      }

      if (!directory.isDirectory())
      {
         throw new IOException(
          String.format("Not a directory: %s", directory.toString()));
      }

      directory = directory.getCanonicalFile();

      File cwd = new File(invoker.getSystemProperty("user.dir", "."));

      if (!cwd.equals(directory) && !invoker.isFileInTree(directory, cwd))
      {
         throw new IOException(
          String.format("Walk must start in current working directory path: %s",
           cwd.toString()));
      }

      Path start = directory.toPath();

      FileWalkVisitor visitor = new FileWalkVisitor(invoker, regex,
       new FilePathSortComparator(sortType));

      Files.walkFileTree(start, visitor);

      Vector<Path> result = visitor.getList();

      StringBuilder builder = new StringBuilder();

      for (Path path : result)
      {
         if (builder.length() > 0)
         {
            builder.append(separator);
         }

         builder.append(
           invoker.escapeFileName(start.relativize(path).toString()));
      }

      return builder.toString();
   }

   private TeXOSQuery invoker;
   private Pattern pattern;
   private Vector<Path> result;
   private Comparator<Path> comparator;
}