OSDN Git Service

Add user qss directory.
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / config / FileManager.java
1 package cx.fbn.nevernote.config;
2
3 import java.io.File;
4 import java.io.FileFilter;
5 import java.util.regex.Pattern;
6
7 /**
8  * Provides access to NeverNote standard runtime directories.
9  *
10  * @author Nick Clarke
11  */
12 public class FileManager {
13
14     private static final Pattern ALL_PATH_SEPARATORS_REGEX = Pattern.compile("[/\\\\]");
15
16     private final String programDirPath;
17     private final File programDir;
18     
19     private final String homeDirPath;
20     private final File homeDir;
21
22     private final String dbDirPath;
23     private final File dbDir;
24
25     private final File logsDir;
26
27     private final String imagesDirPath;
28     private final File imagesDir;
29
30     private final String spellDirPath;
31     private final File spellDir;
32     
33     private final String spellDirPathUser;
34     private final File spellDirUser;
35     
36     private final String qssDirPath;
37     private final File qssDir;
38     
39     private final String qssDirPathUser;
40     private final File qssDirUser;
41
42     private final String resDirPath;
43     private final File resDir;
44
45     private final File xmlDir;
46
47     private final String translateDirPath;
48     private final File translateDir;
49
50     /**
51      * Check or create the db, log and res directories.
52      *
53      * @param homeDirPath the installation dir containing db/log/res directories, must exist
54      * @throws InitializationException for missing directories or file permissions problems
55      */
56     public FileManager(String homeDirPath, String programDirPath) throws InitializationException {
57         if (homeDirPath == null) {
58             throw new IllegalArgumentException("homeDirPath must not be null");
59         }
60         if (programDirPath == null) {
61             throw new IllegalArgumentException("programDirPath must not be null");
62         }
63
64         this.homeDir = new File(toPlatformPathSeparator(homeDirPath));
65         this.programDir = new File(toPlatformPathSeparator(programDirPath));
66         createDirOrCheckWriteable(homeDir);
67         this.homeDirPath = slashTerminatePath(homeDir.getPath());
68         this.programDirPath = slashTerminatePath(programDir.getPath());
69         
70         // Read-only
71         imagesDir = new File(programDir, "images");
72         checkExistingReadableDir(imagesDir);
73         imagesDirPath = slashTerminatePath(imagesDir.getPath());
74
75         qssDir = new File(programDir, "qss");
76         checkExistingReadableDir(qssDir);
77         qssDirPath = slashTerminatePath(qssDir.getPath());
78         
79         qssDirUser = new File(homeDir, "qss");
80         createDirOrCheckWriteable(qssDirUser);
81         qssDirPathUser = slashTerminatePath(qssDirUser.getPath());
82
83         spellDir = new File(programDir, "spell");
84         checkExistingReadableDir(spellDir);
85         spellDirPath = slashTerminatePath(spellDir.getPath());
86         
87
88         spellDirUser = new File(homeDir, "spell");
89         createDirOrCheckWriteable(spellDirUser);
90         spellDirPathUser = slashTerminatePath(spellDirUser.getPath());
91         
92         xmlDir = new File(programDir, "xml");
93         checkExistingReadableDir(xmlDir);
94
95         translateDir = new File(programDir, "translations");
96         checkExistingReadableDir(translateDir);
97         translateDirPath= slashTerminatePath(translateDir.getPath());
98
99         // Read-write
100         dbDir = new File(homeDir, "db");
101         createDirOrCheckWriteable(dbDir);
102         dbDirPath = slashTerminatePath(dbDir.getPath());
103
104         logsDir = new File(homeDir, "logs");
105         createDirOrCheckWriteable(logsDir);
106
107         resDir = new File(homeDir, "res");
108         createDirOrCheckWriteable(resDir);
109         resDirPath = slashTerminatePath(resDir.getPath());
110     }
111
112     /**
113      * Get a file below the base user home directory.
114      */
115     public File getProgramDirFile(String relativePath) {
116         return new File(programDir, toPlatformPathSeparator(relativePath));
117     }
118
119     /**
120      * Get a path below the base user home directory, using native {@link File#separator}.
121      * This will contain backslashes on Windows.
122      */
123     public String getProgramDirPath(String relativePath) {
124         return programDirPath + toPlatformPathSeparator(relativePath);
125     }
126     
127     /**
128      * Get a file below the base user home directory.
129      */
130     public File getHomeDirFile(String relativePath) {
131         return new File(homeDir, toPlatformPathSeparator(relativePath));
132     }
133
134     /**
135      * Get a path below the base user home directory, using native {@link File#separator}.
136      * This will contain backslashes on Windows.
137      */
138     public String getHomeDirPath(String relativePath) {
139         return homeDirPath + toPlatformPathSeparator(relativePath);
140     }
141
142     /**
143      * Get a file below the 'db' directory.
144      */
145     public File getDbDirFile(String relativePath) {
146         return new File(dbDir, toPlatformPathSeparator(relativePath));
147     }
148
149     /**
150      * Get a path below the 'spell' directory, using native {@link File#separator}.
151      * This will contain backslashes on Windows.
152      */
153     public String getSpellDirPath(String relativePath) {
154         return dbDirPath + toPlatformPathSeparator(relativePath);
155     }
156
157     /**
158      * Get a file below the 'spell' directory.
159      */
160     public File getSpellDirFile(String relativePath) {
161         return new File(spellDir, toPlatformPathSeparator(relativePath));
162     }
163     
164     /** 
165      * Get the spell directory for the jazzy word list
166      */
167     public String getSpellDirPath() {
168         return spellDirPath;
169     }
170
171     /**
172      * Get a file below the 'spell' directory for user dictionaries.
173      */
174     public File getSpellDirFileUser(String relativePath) {
175         return new File(spellDirUser, toPlatformPathSeparator(relativePath));
176     }
177     
178     /** 
179      * Get the spell directory for the jazzy word list (user dictionary).
180      */
181     public String getSpellDirPathUser() {
182         return spellDirPathUser;
183     }
184     
185     /**
186      * Get a path below the 'db' directory, using native {@link File#separator}.
187      * This will contain backslashes on Windows.
188      */    
189     public String getDbDirPath(String relativePath) {
190         return dbDirPath + toPlatformPathSeparator(relativePath);
191     }
192     
193     /**
194      * Get a file below the 'images' directory.
195      */
196     public File getImageDirFile(String relativePath) {
197         return new File(imagesDir, toPlatformPathSeparator(relativePath));
198     }
199
200     /**
201      * Get a path below the 'images' directory, using native {@link File#separator}.
202      * This will contain backslashes on Windows.
203      */
204     public String getImageDirPath(String relativePath) {
205         return imagesDirPath + toPlatformPathSeparator(relativePath);
206     }
207
208     /**
209      * Get a file below the 'logs' directory.
210      */
211     public File getLogsDirFile(String relativePath) {
212         return new File(logsDir, toPlatformPathSeparator(relativePath));
213     }
214
215     /**
216      * Get a path below the 'qss' directory, using native {@link File#separator}.
217      * This will contain backslashes on Windows.
218      */
219     public String getQssDirPath(String relativePath) {
220         return qssDirPath + toPlatformPathSeparator(relativePath);
221     }
222
223     /**
224      * Get a path below the 'qss' directory, using native {@link File#separator}.
225      * This will contain backslashes on Windows.
226      */
227     public String getQssDirPathUser(String relativePath) {
228         return qssDirPathUser + toPlatformPathSeparator(relativePath);
229     }
230     
231     /**
232      * Get a path to the 'res' directory, terminated with native {@link File#separator}.
233      * This will contain backslashes on Windows.
234      */
235     public String getResDirPath() {
236         return resDirPath;
237     }
238
239     /**
240      * Get a path below the 'res' directory, using native {@link File#separator}.
241      * This will contain backslashes on Windows.
242      */
243     public String getResDirPath(String relativePath) {
244         return resDirPath + toPlatformPathSeparator(relativePath);
245     }
246
247     /**
248      * Get a file below the 'xml' directory.
249      */
250     public File getXMLDirFile(String relativePath) {
251         return new File(xmlDir, toPlatformPathSeparator(relativePath));
252     }
253
254     /**
255      * Get a path below the 'translate' directory, using native {@link File#separator}.
256      * This will contain backslashes on Windows.
257      */
258     public String getTranslateFilePath(String relativePath) {
259         return translateDirPath + toPlatformPathSeparator(relativePath);
260     }
261
262     private static String toPlatformPathSeparator(String relativePath) {
263         // Sometimes a space in the file name comes across as a %20.  This is to put it back as a space.
264         relativePath = relativePath.replace("%20", " ");
265                 return ALL_PATH_SEPARATORS_REGEX.matcher(relativePath).replaceAll(
266                                 // Must double-escape backslashes,
267                                 // because they have special meaning in the replacement string of Matcher.replaceAll
268                                 (File.separator.equals("\\") ? "\\\\" : File.separator));
269     }
270
271     private static String slashTerminatePath(String path) {
272         if (!path.substring(path.length() - 1).equals(File.separator)) {
273             return path + File.separator;
274         }
275         return path;
276     }
277
278     /**
279      * Delete first-level files (but not directories) from the directory.
280      *
281      * @throws InitializationException for file deletion failures
282      */
283     private static void deleteTopLevelFiles(File dir, boolean exitOnFail) throws InitializationException {
284         File[] toDelete = dir.listFiles(new FileFilter() {
285             @Override
286             public boolean accept(File pathname) {
287                 return pathname.isFile();
288             }
289         });
290         for (File f : toDelete) {
291             if (!f.delete() && exitOnFail) {
292                 throw new InitializationException("Failed to delete file: '" + f + "'");
293             }
294         }
295     }
296
297     /**
298      * @throws InitializationException for bad file permissions, or a file instead of a directory
299      */
300     private static void createDirOrCheckWriteable(File dir) throws InitializationException {
301         if (dir.isDirectory()) {
302             // Dir exists, check permissions
303             if (!dir.canRead()) {
304                 throw new InitializationException("Directory '" + dir + "' does not have read permission");
305             }
306             if (!dir.canWrite()) {
307                 throw new InitializationException("Directory '" + dir + "' does not have write permission");
308             }
309         } else if (!dir.exists()) {
310             if (!dir.mkdirs()) {
311                 throw new InitializationException("Failed to create directory '" + dir + "'");
312             }
313         } else {
314             throw new InitializationException("Expected directory '" + dir + "' but found a file instead");
315         }
316     }
317
318     /**
319      * @throws InitializationException if non-existent, bad file permissions, or a file instead of a directory
320      */
321     private static void checkExistingReadableDir(File dir) throws InitializationException {
322         if (dir.isDirectory()) {
323             // Dir exists, check permissions
324             if (!dir.canRead()) {
325                 throw new InitializationException("Directory '" + dir + "' does not have read permission");
326             }
327         } else if (!dir.exists()) {
328             throw new InitializationException("Directory '" + dir + "' does not exist");
329         } else {
330             throw new InitializationException("Expected directory '" + dir + "' but found a file instead");
331         }
332     }
333
334     /**
335      * @throws InitializationException if non-existent, bad file permissions, or a file instead of a directory
336      */
337     @SuppressWarnings("unused")
338         private static void checkExistingWriteableDir(File dir) throws InitializationException {
339         checkExistingReadableDir(dir);
340         if (!dir.canWrite()) {
341             throw new InitializationException("Directory '" + dir + "' does not have write permission");
342         }
343     }
344
345     /**
346      * Called at startup to purge files from 'res' directory.
347      */
348     public void purgeResDirectory(boolean exitOnFail) throws InitializationException {
349         deleteTopLevelFiles(resDir, exitOnFail);
350     }
351 }