OSDN Git Service

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