Mercurial > jhg
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 } |
