Mercurial > hg4j
comparison src/org/tmatesoft/hg/util/RegularFileInfo.java @ 413:7f27122011c3
Support and respect for symbolic links and executable flag, with /bin/ls backed implementation to discover these
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Wed, 21 Mar 2012 20:40:28 +0100 |
parents | 981f9f50bb6c |
children | 48f993aa2f41 |
comparison
equal
deleted
inserted
replaced
406:d56ea1a2537a | 413:7f27122011c3 |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2011 TMate Software Ltd | 2 * Copyright (c) 2011-2012 TMate Software Ltd |
3 * | 3 * |
4 * This program is free software; you can redistribute it and/or modify | 4 * This program is free software; you can redistribute it and/or modify |
5 * it under the terms of the GNU General Public License as published by | 5 * it under the terms of the GNU General Public License as published by |
6 * the Free Software Foundation; version 2 of the License. | 6 * the Free Software Foundation; version 2 of the License. |
7 * | 7 * |
19 import java.io.File; | 19 import java.io.File; |
20 import java.io.FileInputStream; | 20 import java.io.FileInputStream; |
21 import java.io.FileNotFoundException; | 21 import java.io.FileNotFoundException; |
22 import java.io.IOException; | 22 import java.io.IOException; |
23 import java.nio.ByteBuffer; | 23 import java.nio.ByteBuffer; |
24 import java.nio.channels.ClosedChannelException; | |
24 import java.nio.channels.ReadableByteChannel; | 25 import java.nio.channels.ReadableByteChannel; |
25 | 26 |
26 import org.tmatesoft.hg.internal.StreamLogFacility; | 27 import org.tmatesoft.hg.internal.StreamLogFacility; |
27 | 28 |
28 /** | 29 /** |
29 * | 30 * |
30 * @author Artem Tikhomirov | 31 * @author Artem Tikhomirov |
31 * @author TMate Software Ltd. | 32 * @author TMate Software Ltd. |
32 */ | 33 */ |
33 public class RegularFileInfo implements FileInfo { | 34 public class RegularFileInfo implements FileInfo { |
35 private final boolean supportsExec, supportsLink; | |
36 private final RegularFileStats fileFlagsHelper; // null if both supportsLink and supportExec are false | |
34 private File file; | 37 private File file; |
35 | 38 |
36 public RegularFileInfo() { | 39 public RegularFileInfo() { |
40 this(false, false); | |
41 } | |
42 public RegularFileInfo(boolean supportExecFlag, boolean supportSymlink) { | |
43 supportsLink = supportSymlink; | |
44 supportsExec = supportExecFlag; | |
45 if (supportSymlink || supportExecFlag) { | |
46 fileFlagsHelper = new RegularFileStats(); | |
47 } else { | |
48 fileFlagsHelper = null; | |
49 } | |
37 } | 50 } |
38 | 51 |
39 public void init(File f) { | 52 public void init(File f) { |
40 file = f; | 53 file = f; |
54 if (fileFlagsHelper != null) { | |
55 fileFlagsHelper.init(file); | |
56 } | |
41 } | 57 } |
42 | 58 |
43 public boolean exists() { | 59 public boolean exists() { |
44 return file.canRead() && file.isFile(); | 60 // java.io.File for symlinks without proper target says it doesn't exist. |
61 // since we found this symlink in directory listing, it's safe to say it exists just based on the fact it's link | |
62 return isSymlink() || (file.canRead() && file.isFile()); | |
45 } | 63 } |
46 | 64 |
47 public int lastModified() { | 65 public int lastModified() { |
66 // TODO post-1.0 for symlinks, this returns incorrect mtime of the target file, not that of link itself | |
67 // Besides, timestame if link points to non-existing file is 0. | |
68 // However, it result only in slowdown in WCStatusCollector, as it need to perform additional content check | |
48 return (int) (file.lastModified() / 1000); | 69 return (int) (file.lastModified() / 1000); |
49 } | 70 } |
50 | 71 |
51 public long length() { | 72 public long length() { |
73 if (isSymlink()) { | |
74 return getLinkTargetBytes().length; | |
75 } | |
52 return file.length(); | 76 return file.length(); |
53 } | 77 } |
54 | 78 |
55 public ReadableByteChannel newInputChannel() { | 79 public ReadableByteChannel newInputChannel() { |
56 try { | 80 try { |
57 return new FileInputStream(file).getChannel(); | 81 if (isSymlink()) { |
82 return new ByteArrayReadableChannel(getLinkTargetBytes()); | |
83 } else { | |
84 return new FileInputStream(file).getChannel(); | |
85 } | |
58 } catch (FileNotFoundException ex) { | 86 } catch (FileNotFoundException ex) { |
59 StreamLogFacility.newDefault().debug(getClass(), ex, null); | 87 StreamLogFacility.newDefault().debug(getClass(), ex, null); |
60 // shall not happen, provided this class is used correctly | 88 // shall not happen, provided this class is used correctly |
61 return new ReadableByteChannel() { | 89 return new ByteArrayReadableChannel(null); |
62 | |
63 public boolean isOpen() { | |
64 return true; | |
65 } | |
66 | |
67 public void close() throws IOException { | |
68 } | |
69 | |
70 public int read(ByteBuffer dst) throws IOException { | |
71 // EOF right away | |
72 return -1; | |
73 } | |
74 }; | |
75 } | 90 } |
76 } | 91 } |
77 | 92 |
93 public boolean isExecutable() { | |
94 return supportsExec && fileFlagsHelper.isExecutable(); | |
95 } | |
96 | |
97 public boolean isSymlink() { | |
98 return supportsLink && fileFlagsHelper.isSymlink(); | |
99 } | |
100 | |
101 private byte[] getLinkTargetBytes() { | |
102 assert isSymlink(); | |
103 // no idea what encoding Mercurial uses for link targets, assume platform native is ok | |
104 return fileFlagsHelper.getSymlinkTarget().getBytes(); | |
105 } | |
106 | |
107 | |
108 private static class ByteArrayReadableChannel implements ReadableByteChannel { | |
109 private final byte[] data; | |
110 private boolean closed = false; // initially open | |
111 private int firstAvailIndex = 0; | |
112 | |
113 ByteArrayReadableChannel(byte[] dataToStream) { | |
114 data = dataToStream; | |
115 } | |
116 | |
117 public boolean isOpen() { | |
118 return !closed; | |
119 } | |
120 | |
121 public void close() throws IOException { | |
122 closed = true; | |
123 } | |
124 | |
125 public int read(ByteBuffer dst) throws IOException { | |
126 if (closed) { | |
127 throw new ClosedChannelException(); | |
128 } | |
129 int remainingBytes = data.length - firstAvailIndex; | |
130 if (data == null || remainingBytes == 0) { | |
131 // EOF right away | |
132 return -1; | |
133 } | |
134 int x = Math.min(dst.remaining(), remainingBytes); | |
135 for (int i = firstAvailIndex, lim = firstAvailIndex + x; i < lim; i++) { | |
136 dst.put(data[i]); | |
137 } | |
138 firstAvailIndex += x; | |
139 return x; | |
140 } | |
141 } | |
78 } | 142 } |