1 package cx.fbn.nevernote.config;
4 import java.io.FileFilter;
5 import java.util.regex.Pattern;
8 * Provides access to NeverNote standard runtime directories.
12 public class FileManager {
14 private static final Pattern ALL_PATH_SEPARATORS_REGEX = Pattern.compile("[/\\\\]");
16 private final String programDirPath;
17 private final File programDir;
19 private final String homeDirPath;
20 private final File homeDir;
22 private final String dbDirPath;
23 private final File dbDir;
25 private final File logsDir;
27 private final String imagesDirPath;
28 private final File imagesDir;
30 private final String spellDirPath;
31 private final File spellDir;
33 private final String qssDirPath;
34 private final File qssDir;
36 private final String resDirPath;
37 private final File resDir;
39 private final File xmlDir;
42 * Check or create the db, log and res directories.
44 * @param homeDirPath the installation dir containing db/log/res directories, must exist
45 * @throws InitializationException for missing directories or file permissions problems
47 public FileManager(String homeDirPath, String programDirPath) throws InitializationException {
48 if (homeDirPath == null) {
49 throw new IllegalArgumentException("homeDirPath must not be null");
51 if (programDirPath == null) {
52 throw new IllegalArgumentException("programDirPath must not be null");
55 this.homeDir = new File(toPlatformPathSeparator(homeDirPath));
56 this.programDir = new File(toPlatformPathSeparator(programDirPath));
57 createDirOrCheckWriteable(homeDir);
58 this.homeDirPath = slashTerminatePath(homeDir.getPath());
59 this.programDirPath = slashTerminatePath(programDir.getPath());
62 imagesDir = new File(programDir, "images");
63 checkExistingReadableDir(imagesDir);
64 imagesDirPath = slashTerminatePath(imagesDir.getPath());
66 qssDir = new File(programDir, "qss");
67 checkExistingReadableDir(qssDir);
68 qssDirPath = slashTerminatePath(qssDir.getPath());
70 spellDir = new File(programDir, "spell");
71 checkExistingReadableDir(spellDir);
72 spellDirPath = slashTerminatePath(spellDir.getPath());
74 xmlDir = new File(programDir, "xml");
75 checkExistingReadableDir(xmlDir);
78 dbDir = new File(homeDir, "db");
79 createDirOrCheckWriteable(dbDir);
80 dbDirPath = slashTerminatePath(dbDir.getPath());
82 logsDir = new File(homeDir, "logs");
83 createDirOrCheckWriteable(logsDir);
85 resDir = new File(homeDir, "res");
86 createDirOrCheckWriteable(resDir);
87 resDirPath = slashTerminatePath(resDir.getPath());
91 * Get a file below the base user home directory.
93 public File getProgramDirFile(String relativePath) {
94 return new File(programDir, toPlatformPathSeparator(relativePath));
98 * Get a path below the base user home directory, using native {@link File#separator}.
99 * This will contain backslashes on Windows.
101 public String getProgramDirPath(String relativePath) {
102 return programDirPath + toPlatformPathSeparator(relativePath);
106 * Get a file below the base user home directory.
108 public File getHomeDirFile(String relativePath) {
109 return new File(homeDir, toPlatformPathSeparator(relativePath));
113 * Get a path below the base user home directory, using native {@link File#separator}.
114 * This will contain backslashes on Windows.
116 public String getHomeDirPath(String relativePath) {
117 return homeDirPath + toPlatformPathSeparator(relativePath);
121 * Get a file below the 'db' directory.
123 public File getDbDirFile(String relativePath) {
124 return new File(dbDir, toPlatformPathSeparator(relativePath));
128 * Get a path below the 'db' directory, using native {@link File#separator}.
129 * This will contain backslashes on Windows.
131 public String getSpellDirPath(String relativePath) {
132 return dbDirPath + toPlatformPathSeparator(relativePath);
136 * Get a file below the 'spell' directory.
138 public File getSpellDirFile(String relativePath) {
139 return new File(dbDir, toPlatformPathSeparator(relativePath));
143 * Get the spell directory for the jazzy word list
145 public String getSpellDirPath() {
150 * Get a path below the 'spell' directory, using native {@link File#separator}.
151 * This will contain backslashes on Windows.
153 public String getDbDirPath(String relativePath) {
154 return dbDirPath + toPlatformPathSeparator(relativePath);
158 * Get a file below the 'images' directory.
160 public File getImageDirFile(String relativePath) {
161 return new File(imagesDir, toPlatformPathSeparator(relativePath));
165 * Get a path below the 'images' directory, using native {@link File#separator}.
166 * This will contain backslashes on Windows.
168 public String getImageDirPath(String relativePath) {
169 return imagesDirPath + toPlatformPathSeparator(relativePath);
173 * Get a file below the 'logs' directory.
175 public File getLogsDirFile(String relativePath) {
176 return new File(logsDir, toPlatformPathSeparator(relativePath));
180 * Get a path below the 'qss' directory, using native {@link File#separator}.
181 * This will contain backslashes on Windows.
183 public String getQssDirPath(String relativePath) {
184 return qssDirPath + toPlatformPathSeparator(relativePath);
188 * Get a path to the 'res' directory, terminated with native {@link File#separator}.
189 * This will contain backslashes on Windows.
191 public String getResDirPath() {
196 * Get a path below the 'res' directory, using native {@link File#separator}.
197 * This will contain backslashes on Windows.
199 public String getResDirPath(String relativePath) {
200 return resDirPath + toPlatformPathSeparator(relativePath);
204 * Get a file below the 'xml' directory.
206 public File getXMLDirFile(String relativePath) {
207 return new File(xmlDir, toPlatformPathSeparator(relativePath));
210 private static String toPlatformPathSeparator(String relativePath) {
211 // Sometimes a space in the file name comes across as a %20. This is to put it back as a space.
212 relativePath = relativePath.replace("%20", " ");
213 return ALL_PATH_SEPARATORS_REGEX.matcher(relativePath).replaceAll(
214 // Must double-escape backslashes,
215 // because they have special meaning in the replacement string of Matcher.replaceAll
216 (File.separator.equals("\\") ? "\\\\" : File.separator));
219 private static String slashTerminatePath(String path) {
220 if (!path.substring(path.length() - 1).equals(File.separator)) {
221 return path + File.separator;
227 * Delete first-level files (but not directories) from the directory.
229 * @throws InitializationException for file deletion failures
231 private static void deleteTopLevelFiles(File dir) throws InitializationException {
232 File[] toDelete = dir.listFiles(new FileFilter() {
234 public boolean accept(File pathname) {
235 return pathname.isFile();
238 for (File f : toDelete) {
240 throw new InitializationException("Failed to delete file: '" + f + "'");
246 * @throws InitializationException for bad file permissions, or a file instead of a directory
248 private static void createDirOrCheckWriteable(File dir) throws InitializationException {
249 if (dir.isDirectory()) {
250 // Dir exists, check permissions
251 if (!dir.canRead()) {
252 throw new InitializationException("Directory '" + dir + "' does not have read permission");
254 if (!dir.canWrite()) {
255 throw new InitializationException("Directory '" + dir + "' does not have write permission");
257 } else if (!dir.exists()) {
259 throw new InitializationException("Failed to create directory '" + dir + "'");
262 throw new InitializationException("Expected directory '" + dir + "' but found a file instead");
267 * @throws InitializationException if non-existent, bad file permissions, or a file instead of a directory
269 private static void checkExistingReadableDir(File dir) throws InitializationException {
270 if (dir.isDirectory()) {
271 // Dir exists, check permissions
272 if (!dir.canRead()) {
273 throw new InitializationException("Directory '" + dir + "' does not have read permission");
275 } else if (!dir.exists()) {
276 throw new InitializationException("Directory '" + dir + "' does not exist");
278 throw new InitializationException("Expected directory '" + dir + "' but found a file instead");
283 * @throws InitializationException if non-existent, bad file permissions, or a file instead of a directory
285 private static void checkExistingWriteableDir(File dir) throws InitializationException {
286 checkExistingReadableDir(dir);
287 if (!dir.canWrite()) {
288 throw new InitializationException("Directory '" + dir + "' does not have write permission");
293 * Called at startup to purge files from 'res' directory.
295 public void purgeResDirectory() throws InitializationException {
296 deleteTopLevelFiles(resDir);