tikhomirov@287: /* tikhomirov@413: * Copyright (c) 2011-2012 TMate Software Ltd tikhomirov@287: * tikhomirov@287: * This program is free software; you can redistribute it and/or modify tikhomirov@287: * it under the terms of the GNU General Public License as published by tikhomirov@287: * the Free Software Foundation; version 2 of the License. tikhomirov@287: * tikhomirov@287: * This program is distributed in the hope that it will be useful, tikhomirov@287: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@287: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@287: * GNU General Public License for more details. tikhomirov@287: * tikhomirov@287: * For information on how to redistribute this software under tikhomirov@287: * the terms of a license other than GNU General Public License tikhomirov@287: * contact TMate Software at support@hg4j.com tikhomirov@287: */ tikhomirov@287: package org.tmatesoft.hg.util; tikhomirov@287: tikhomirov@287: import java.io.File; tikhomirov@287: import java.io.FileInputStream; tikhomirov@287: import java.io.FileNotFoundException; tikhomirov@287: import java.io.IOException; tikhomirov@287: import java.nio.ByteBuffer; tikhomirov@413: import java.nio.channels.ClosedChannelException; tikhomirov@287: import java.nio.channels.ReadableByteChannel; tikhomirov@287: tikhomirov@295: import org.tmatesoft.hg.internal.StreamLogFacility; tikhomirov@295: tikhomirov@287: /** tikhomirov@287: * tikhomirov@287: * @author Artem Tikhomirov tikhomirov@287: * @author TMate Software Ltd. tikhomirov@287: */ tikhomirov@287: public class RegularFileInfo implements FileInfo { tikhomirov@413: private final boolean supportsExec, supportsLink; tikhomirov@413: private final RegularFileStats fileFlagsHelper; // null if both supportsLink and supportExec are false tikhomirov@287: private File file; tikhomirov@287: tikhomirov@287: public RegularFileInfo() { tikhomirov@413: this(false, false); tikhomirov@413: } tikhomirov@413: public RegularFileInfo(boolean supportExecFlag, boolean supportSymlink) { tikhomirov@413: supportsLink = supportSymlink; tikhomirov@413: supportsExec = supportExecFlag; tikhomirov@413: if (supportSymlink || supportExecFlag) { tikhomirov@413: fileFlagsHelper = new RegularFileStats(); tikhomirov@413: } else { tikhomirov@413: fileFlagsHelper = null; tikhomirov@413: } tikhomirov@287: } tikhomirov@287: tikhomirov@287: public void init(File f) { tikhomirov@287: file = f; tikhomirov@413: if (fileFlagsHelper != null) { tikhomirov@413: fileFlagsHelper.init(file); tikhomirov@413: } tikhomirov@287: } tikhomirov@287: tikhomirov@287: public boolean exists() { tikhomirov@413: // java.io.File for symlinks without proper target says it doesn't exist. tikhomirov@413: // since we found this symlink in directory listing, it's safe to say it exists just based on the fact it's link tikhomirov@413: return isSymlink() || (file.canRead() && file.isFile()); tikhomirov@287: } tikhomirov@287: tikhomirov@287: public int lastModified() { tikhomirov@413: // TODO post-1.0 for symlinks, this returns incorrect mtime of the target file, not that of link itself tikhomirov@413: // Besides, timestame if link points to non-existing file is 0. tikhomirov@413: // However, it result only in slowdown in WCStatusCollector, as it need to perform additional content check tikhomirov@287: return (int) (file.lastModified() / 1000); tikhomirov@287: } tikhomirov@287: tikhomirov@287: public long length() { tikhomirov@413: if (isSymlink()) { tikhomirov@413: return getLinkTargetBytes().length; tikhomirov@413: } tikhomirov@287: return file.length(); tikhomirov@287: } tikhomirov@287: tikhomirov@287: public ReadableByteChannel newInputChannel() { tikhomirov@287: try { tikhomirov@413: if (isSymlink()) { tikhomirov@413: return new ByteArrayReadableChannel(getLinkTargetBytes()); tikhomirov@413: } else { tikhomirov@413: return new FileInputStream(file).getChannel(); tikhomirov@413: } tikhomirov@287: } catch (FileNotFoundException ex) { tikhomirov@295: StreamLogFacility.newDefault().debug(getClass(), ex, null); tikhomirov@287: // shall not happen, provided this class is used correctly tikhomirov@413: return new ByteArrayReadableChannel(null); tikhomirov@287: } tikhomirov@287: } tikhomirov@287: tikhomirov@413: public boolean isExecutable() { tikhomirov@413: return supportsExec && fileFlagsHelper.isExecutable(); tikhomirov@413: } tikhomirov@413: tikhomirov@413: public boolean isSymlink() { tikhomirov@413: return supportsLink && fileFlagsHelper.isSymlink(); tikhomirov@413: } tikhomirov@413: tikhomirov@413: private byte[] getLinkTargetBytes() { tikhomirov@413: assert isSymlink(); tikhomirov@413: // no idea what encoding Mercurial uses for link targets, assume platform native is ok tikhomirov@413: return fileFlagsHelper.getSymlinkTarget().getBytes(); tikhomirov@413: } tikhomirov@413: tikhomirov@413: tikhomirov@413: private static class ByteArrayReadableChannel implements ReadableByteChannel { tikhomirov@413: private final byte[] data; tikhomirov@413: private boolean closed = false; // initially open tikhomirov@413: private int firstAvailIndex = 0; tikhomirov@413: tikhomirov@413: ByteArrayReadableChannel(byte[] dataToStream) { tikhomirov@413: data = dataToStream; tikhomirov@413: } tikhomirov@413: tikhomirov@413: public boolean isOpen() { tikhomirov@413: return !closed; tikhomirov@413: } tikhomirov@413: tikhomirov@413: public void close() throws IOException { tikhomirov@413: closed = true; tikhomirov@413: } tikhomirov@413: tikhomirov@413: public int read(ByteBuffer dst) throws IOException { tikhomirov@413: if (closed) { tikhomirov@413: throw new ClosedChannelException(); tikhomirov@413: } tikhomirov@413: int remainingBytes = data.length - firstAvailIndex; tikhomirov@413: if (data == null || remainingBytes == 0) { tikhomirov@413: // EOF right away tikhomirov@413: return -1; tikhomirov@413: } tikhomirov@413: int x = Math.min(dst.remaining(), remainingBytes); tikhomirov@413: for (int i = firstAvailIndex, lim = firstAvailIndex + x; i < lim; i++) { tikhomirov@413: dst.put(data[i]); tikhomirov@413: } tikhomirov@413: firstAvailIndex += x; tikhomirov@413: return x; tikhomirov@413: } tikhomirov@413: } tikhomirov@287: }