PageRenderTime 49ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sys/java/fan/sys/LocalFile.java

https://bitbucket.org/bedlaczech/fan-1.0
Java | 488 lines | 377 code | 64 blank | 47 comment | 87 complexity | 0ed04ef93900de988b67baca5b7424e2 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //
  2. // Copyright (c) 2006, Brian Frank and Andy Frank
  3. // Licensed under the Academic Free License version 3.0
  4. //
  5. // History:
  6. // 26 Mar 06 Brian Frank Creation
  7. //
  8. package fan.sys;
  9. import java.io.Closeable;
  10. import java.io.FileDescriptor;
  11. import java.io.RandomAccessFile;
  12. import java.nio.MappedByteBuffer;
  13. import java.nio.channels.FileChannel;
  14. import java.nio.channels.FileChannel.MapMode;
  15. /**
  16. * LocalFile represents a file or directory in the local file system.
  17. */
  18. public class LocalFile
  19. extends File
  20. {
  21. //////////////////////////////////////////////////////////////////////////
  22. // Conversions
  23. //////////////////////////////////////////////////////////////////////////
  24. public static Uri fileToUri(java.io.File file, boolean isDir, String scheme)
  25. {
  26. String path = file.getPath();
  27. int len = path.length();
  28. StringBuilder s = new StringBuilder(path.length()+2);
  29. // if scheme was specified
  30. if (scheme != null) s.append(scheme).append(':');
  31. // deal with Windoze drive name
  32. if (len > 2 && path.charAt(1) == ':' && path.charAt(0) != '/')
  33. s.append('/');
  34. // map characters
  35. for (int i=0; i<len; ++i)
  36. {
  37. int c = path.charAt(i);
  38. switch (c)
  39. {
  40. case '?':
  41. case '#': s.append('\\').append((char)c); break;
  42. case '\\': s.append('/'); break;
  43. default: s.append((char)c);
  44. }
  45. }
  46. // add trailing slash if not present
  47. if (isDir && (s.length() == 0 || s.charAt(s.length()-1) != '/'))
  48. s.append('/');
  49. return Uri.fromStr(s.toString());
  50. }
  51. public static java.io.File uriToFile(Uri uri)
  52. {
  53. if (uri.scheme() != null && !uri.scheme().equals("file"))
  54. throw ArgErr.make("Invalid Uri scheme for local file: " + uri);
  55. return new java.io.File(uriToPath(uri));
  56. }
  57. public static String uriToPath(Uri uri)
  58. {
  59. String path = uri.pathStr();
  60. int len = path.length();
  61. StringBuilder s = null;
  62. for (int i=0; i<len; ++i)
  63. {
  64. int c = path.charAt(i);
  65. if (c == '\\')
  66. {
  67. if (s == null) { s = new StringBuilder(); s.append(path, 0, i); }
  68. }
  69. else if (s != null) s.append((char)c);
  70. }
  71. return s == null ? path : s.toString();
  72. }
  73. public static String fileNameToUriName(String name)
  74. {
  75. int len = name.length();
  76. StringBuilder s = null;
  77. for (int i=0; i<len; ++i)
  78. {
  79. int c = name.charAt(i);
  80. switch (c)
  81. {
  82. case '?':
  83. case '#':
  84. if (s == null) { s = new StringBuilder(); s.append(name, 0, i); }
  85. s.append('\\').append((char)c);
  86. break;
  87. default:
  88. if (s != null) s.append((char)c);
  89. }
  90. }
  91. return s == null ? name: s.toString();
  92. }
  93. //////////////////////////////////////////////////////////////////////////
  94. // Construction
  95. //////////////////////////////////////////////////////////////////////////
  96. public LocalFile(java.io.File file)
  97. {
  98. this(file, file.isDirectory());
  99. }
  100. public LocalFile(java.io.File file, boolean isDir)
  101. {
  102. this(fileToUri(file, isDir, null), file);
  103. }
  104. public LocalFile(Uri uri, java.io.File file)
  105. {
  106. super(uri);
  107. this.file = file;
  108. if (file.exists())
  109. {
  110. if (file.isDirectory())
  111. {
  112. if (!uri.isDir())
  113. throw IOErr.make("Must use trailing slash for dir: " + uri);
  114. }
  115. else
  116. {
  117. if (uri.isDir())
  118. throw IOErr.make("Cannot use trailing slash for file: " + uri);
  119. }
  120. }
  121. }
  122. //////////////////////////////////////////////////////////////////////////
  123. // Obj
  124. //////////////////////////////////////////////////////////////////////////
  125. public Type typeof() { return Sys.LocalFileType; }
  126. //////////////////////////////////////////////////////////////////////////
  127. // File
  128. //////////////////////////////////////////////////////////////////////////
  129. public boolean exists()
  130. {
  131. return file.exists();
  132. }
  133. public Long size()
  134. {
  135. if (file.isDirectory()) return null;
  136. return Long.valueOf(file.length());
  137. }
  138. public boolean isEmpty()
  139. {
  140. // if file, then route to default implementation
  141. if (!isDir() || !exists()) return super.isEmpty();
  142. // if running 1.6 or older then use raw java.ioFile.list
  143. // to avoid excessive URI mapping overhead
  144. if (Sys.javaVersion < Sys.JAVA_1_7) return file.list().length == 0;
  145. // TODO: if running 1.7 then use new nio.files API to open a
  146. // directory stream iterator; since we still require compiling
  147. // with 1.6 we have to do this with reflection
  148. try
  149. {
  150. // first time thru lookup reflection methods
  151. if (toPathMethod == null)
  152. {
  153. toPathMethod = file.getClass().getMethod("toPath", new Class[0]);
  154. newDirStreamMethod = Class.forName("java.nio.file.Files").getMethod("newDirectoryStream", new Class[] { toPathMethod.getReturnType() } );
  155. }
  156. // Path path = file.toPath()
  157. // Iterable dirStream = Files.newDirStream(path)
  158. Object path = toPathMethod.invoke(file, (Object[])null);
  159. Iterable dirStream = (Iterable)newDirStreamMethod.invoke(null, new Object[] { path });
  160. try
  161. {
  162. return !dirStream.iterator().hasNext();
  163. }
  164. finally
  165. {
  166. ((Closeable)dirStream).close();
  167. }
  168. }
  169. catch (Exception e)
  170. {
  171. e.printStackTrace();
  172. return super.isEmpty();
  173. }
  174. }
  175. private static java.lang.reflect.Method toPathMethod;
  176. private static java.lang.reflect.Method newDirStreamMethod;
  177. public DateTime modified()
  178. {
  179. return DateTime.fromJava(file.lastModified());
  180. }
  181. public void modified(DateTime time)
  182. {
  183. file.setLastModified(time.toJava());
  184. }
  185. public String osPath()
  186. {
  187. return file.getPath();
  188. }
  189. public File parent()
  190. {
  191. Uri parent = uri.parent();
  192. if (parent == null) return null;
  193. return new LocalFile(parent, uriToFile(parent));
  194. }
  195. public List list()
  196. {
  197. java.io.File[] list = file.listFiles();
  198. int len = list == null ? 0 : list.length;
  199. List acc = new List(Sys.FileType, len);
  200. for (int i=0; i<len; ++i)
  201. {
  202. java.io.File f = list[i];
  203. String name = fileNameToUriName(f.getName());
  204. acc.add(new LocalFile(uri.plusName(name, f.isDirectory()), f));
  205. }
  206. return acc;
  207. }
  208. public File normalize()
  209. {
  210. try
  211. {
  212. java.io.File canonical = file.getCanonicalFile();
  213. boolean isDir = canonical.exists() ? canonical.isDirectory() : this.uri.isDir();
  214. Uri uri = fileToUri(canonical, isDir, "file");
  215. return new LocalFile(uri, canonical);
  216. }
  217. catch (java.io.IOException e)
  218. {
  219. throw IOErr.make(e);
  220. }
  221. }
  222. public File plus(Uri uri, boolean checkSlash)
  223. {
  224. return make(this.uri.plus(uri), checkSlash);
  225. }
  226. public FileStore store()
  227. {
  228. try
  229. {
  230. return new LocalFileStore(this.file);
  231. }
  232. catch (java.io.IOException e)
  233. {
  234. throw IOErr.make(e);
  235. }
  236. }
  237. //////////////////////////////////////////////////////////////////////////
  238. // File Management
  239. //////////////////////////////////////////////////////////////////////////
  240. public File create()
  241. {
  242. if (isDir())
  243. createDir();
  244. else
  245. createFile();
  246. return this;
  247. }
  248. private void createFile()
  249. {
  250. if (file.exists())
  251. {
  252. if (file.isDirectory())
  253. throw IOErr.make("Already exists as dir: " + file);
  254. }
  255. java.io.File parent = file.getParentFile();
  256. if (parent != null && !parent.exists())
  257. {
  258. if (!parent.mkdirs())
  259. throw IOErr.make("Cannot create dir: " + parent);
  260. }
  261. try
  262. {
  263. java.io.FileOutputStream out = new java.io.FileOutputStream(file);
  264. out.close();
  265. }
  266. catch (java.io.IOException e)
  267. {
  268. throw IOErr.make(e);
  269. }
  270. }
  271. private void createDir()
  272. {
  273. if (file.exists())
  274. {
  275. if (!file.isDirectory())
  276. throw IOErr.make("Already exists as file: " + file);
  277. }
  278. else
  279. {
  280. if (!file.mkdirs())
  281. throw IOErr.make("Cannot create dir: " + file);
  282. }
  283. }
  284. public File moveTo(File to)
  285. {
  286. if (isDir() != to.isDir())
  287. {
  288. if (isDir())
  289. throw ArgErr.make("moveTo must be dir `" + to + "`");
  290. else
  291. throw ArgErr.make("moveTo must not be dir `" + to + "`");
  292. }
  293. if (!(to instanceof LocalFile))
  294. throw IOErr.make("Cannot move LocalFile to " + to.typeof());
  295. LocalFile dest = (LocalFile)to;
  296. if (dest.exists())
  297. throw IOErr.make("moveTo already exists: " + to);
  298. if (!file.isDirectory())
  299. {
  300. File destParent = dest.parent();
  301. if (destParent != null && !destParent.exists())
  302. destParent.create();
  303. }
  304. if (!file.renameTo(dest.file))
  305. throw IOErr.make("moveTo failed: " + to);
  306. return to;
  307. }
  308. public void delete()
  309. {
  310. if (!exists()) return;
  311. if (file.isDirectory())
  312. {
  313. List kids = list();
  314. for (int i=0; i<kids.sz(); ++i)
  315. ((File)kids.get(i)).delete();
  316. }
  317. if (!file.delete())
  318. throw IOErr.make("Cannot delete: " + file);
  319. }
  320. public File deleteOnExit()
  321. {
  322. file.deleteOnExit();
  323. return this;
  324. }
  325. //////////////////////////////////////////////////////////////////////////
  326. // IO
  327. //////////////////////////////////////////////////////////////////////////
  328. public Buf open(String mode)
  329. {
  330. try
  331. {
  332. return new FileBuf(this, new RandomAccessFile(file, mode));
  333. }
  334. catch (java.io.IOException e)
  335. {
  336. throw IOErr.make(e);
  337. }
  338. }
  339. public Buf mmap(String mode, long pos, Long size)
  340. {
  341. try
  342. {
  343. // map mode
  344. String rw; MapMode mm;
  345. if (mode.equals("r")) { rw = "r"; mm = MapMode.READ_ONLY; }
  346. else if (mode.equals("rw")) { rw = "rw"; mm = MapMode.READ_WRITE; }
  347. else if (mode.equals("p")) { rw = "rw"; mm = MapMode.PRIVATE; }
  348. else throw ArgErr.make("Invalid mode: " + mode);
  349. // if size is null, use file size
  350. if (size == null) size = size();
  351. // traverse the various Java APIs
  352. RandomAccessFile fp = null;
  353. FileChannel chan = null;
  354. try
  355. {
  356. fp = new RandomAccessFile(file, rw);
  357. chan = fp.getChannel();
  358. return new NioBuf(chan.map(mm, pos, size.longValue()));
  359. }
  360. finally
  361. {
  362. if (chan != null) chan.close();
  363. if (fp != null) fp.close();
  364. }
  365. }
  366. catch (java.io.IOException e)
  367. {
  368. throw IOErr.make(e);
  369. }
  370. }
  371. public InStream in(Long bufSize)
  372. {
  373. try
  374. {
  375. return SysInStream.make(new java.io.FileInputStream(file), bufSize);
  376. }
  377. catch (java.io.IOException e)
  378. {
  379. throw IOErr.make(e);
  380. }
  381. }
  382. public OutStream out(boolean append, Long bufSize)
  383. {
  384. try
  385. {
  386. java.io.File parent = file.getParentFile();
  387. if (parent != null && !parent.exists()) parent.mkdirs();
  388. java.io.FileOutputStream fout = new java.io.FileOutputStream(file, append);
  389. java.io.OutputStream bout = SysOutStream.toBuffered(fout, bufSize);
  390. return new LocalFileOutStream(bout, fout.getFD());
  391. }
  392. catch (java.io.IOException e)
  393. {
  394. throw IOErr.make(e);
  395. }
  396. }
  397. static class LocalFileOutStream extends SysOutStream
  398. {
  399. LocalFileOutStream(java.io.OutputStream out, FileDescriptor fd)
  400. {
  401. super(out);
  402. this.fd = fd;
  403. }
  404. public OutStream sync()
  405. {
  406. try
  407. {
  408. flush();
  409. fd.sync();
  410. return this;
  411. }
  412. catch (java.io.IOException e)
  413. {
  414. throw IOErr.make(e);
  415. }
  416. }
  417. FileDescriptor fd;
  418. }
  419. public java.io.File toJava() { return file; }
  420. //////////////////////////////////////////////////////////////////////////
  421. // Fields
  422. //////////////////////////////////////////////////////////////////////////
  423. final java.io.File file;
  424. }