git4idea: Added binary process handler and made use of it for getting file content

This commit is contained in:
Constantine Plotnikov
2010-06-30 00:05:24 +04:00
parent 8f40a1a76b
commit c1bd862d78
4 changed files with 221 additions and 15 deletions
@@ -24,8 +24,8 @@ import com.intellij.openapi.vcs.changes.CurrentContentRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcsUtil.VcsUtil;
import git4idea.commands.GitBinaryHandler;
import git4idea.commands.GitCommand;
import git4idea.commands.GitSimpleHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -69,12 +69,13 @@ public class GitContentRevision implements ContentRevision {
return null;
}
VirtualFile root = GitUtil.getGitRoot(myFile);
GitSimpleHandler h = new GitSimpleHandler(myProject, root, GitCommand.SHOW);
GitBinaryHandler h = new GitBinaryHandler(myProject, root, GitCommand.SHOW);
h.setCharset(myCharset);
h.setNoSSH(true);
h.setSilent(true);
h.addParameters(myRevision.getRev() + ":" + GitUtil.relativePath(root, myFile));
return h.run();
byte[] result = h.run();
return new String(result, h.getCharset());
}
@NotNull
@@ -21,13 +21,12 @@ import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.history.VcsFileRevision;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.commands.GitBinaryHandler;
import git4idea.commands.GitCommand;
import git4idea.commands.GitSimpleHandler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Date;
@@ -94,18 +93,11 @@ public class GitFileRevision implements VcsFileRevision, Comparable<VcsFileRevis
public synchronized void loadContent() throws VcsException {
final VirtualFile root = GitUtil.getGitRoot(path);
GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.SHOW);
GitBinaryHandler h = new GitBinaryHandler(project, root, GitCommand.SHOW);
h.setNoSSH(true);
h.setCharset(BIN_ENCODING);
h.setSilent(true);
h.addParameters(revision.getRev() + ":" + GitUtil.relativePath(root, path));
String result = h.run();
try {
content = result.getBytes(BIN_ENCODING.name());
}
catch (UnsupportedEncodingException e) {
throw new VcsException("Unable to locate encoding: " + BIN_ENCODING.name() + " Reason: " + e.toString(), e);
}
content = h.run();
}
public synchronized byte[] getContent() throws IOException {
@@ -0,0 +1,213 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package git4idea.commands;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitUtil;
import git4idea.GitVcs;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
/**
* The handler that allows consuming binary data as byte array
*/
public class GitBinaryHandler extends GitHandler {
/**
* The logger
*/
private static final Logger LOG = Logger.getInstance(GitBinaryHandler.class.getName());
/**
* Stdout stream
*/
private final ByteArrayOutputStream myStdout = new ByteArrayOutputStream();
/**
* Stderr stream
*/
private final ByteArrayOutputStream myStderr = new ByteArrayOutputStream();
/**
* The semaphore that waits for stream processing
*/
private final Semaphore mySteamSemaphore = new Semaphore(0);
/**
* The size of buffer to use
*/
private static final int BUFFER_SIZE = 8 * 1024;
/**
* The exception to use
*/
private AtomicReference<VcsException> myException = new AtomicReference<VcsException>();
/**
* A constructor
*
* @param project a project
* @param directory a process directory
* @param command a command to execute (if empty string, the parameter is ignored)
*/
protected GitBinaryHandler(@NotNull Project project, @NotNull File directory, @NotNull GitCommand command) {
super(project, directory, command);
}
/**
* A constructor
*
* @param project a project
* @param vcsRoot a vcs root
* @param command a command to execute (if empty string, the parameter is ignored)
*/
public GitBinaryHandler(final Project project, final VirtualFile vcsRoot, final GitCommand command) {
super(project, vcsRoot, command);
}
/**
* {@inheritDoc}
*/
@Override
protected void startHandlingStreams() {
handleStream(myProcess.getErrorStream(), myStderr);
handleStream(myProcess.getInputStream(), myStdout);
}
/**
* Handle the single stream
*
* @param in the standard input
* @param out the standard output
*/
private void handleStream(final InputStream in, final ByteArrayOutputStream out) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
byte[] buffer = new byte[BUFFER_SIZE];
while (true) {
int rc = 0;
rc = in.read(buffer);
if (rc == -1) {
break;
}
out.write(buffer, 0, rc);
}
}
catch (IOException e) {
//noinspection ThrowableInstanceNeverThrown
if (!myException.compareAndSet(null, new VcsException("Stream IO problem", e))) {
LOG.error("Problem reading stream", e);
}
}
finally {
mySteamSemaphore.release(1);
}
}
}, "Stream copy thread");
t.setDaemon(true);
t.start();
}
/**
* {@inheritDoc}
*/
@Override
protected void destroyProcess() {
myProcess.destroy();
}
/**
* {@inheritDoc}
*/
@Override
protected void waitForProcess() {
try {
mySteamSemaphore.acquire(2);
myProcess.waitFor();
int exitCode = myProcess.exitValue();
setExitCode(exitCode);
}
catch (InterruptedException e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Ignoring process exception: ", e);
}
setExitCode(255);
}
listeners().processTerminated(getExitCode());
}
/**
* Run in the current thread and return the data as array
*
* @return the binary data
* @throws VcsException in case of the problem with running git
*/
public byte[] run() throws VcsException {
addListener(new GitHandlerListener() {
@Override
public void processTerminated(int exitCode) {
if (exitCode != 0 && !isIgnoredErrorCode(exitCode)) {
Charset cs = getCharset();
cs = cs == null ? GitUtil.UTF8_CHARSET : cs;
String message = new String(myStderr.toByteArray(), cs);
if (message.length() == 0) {
//noinspection ThrowableResultOfMethodCallIgnored
if (myException.get() != null) {
message = "Process finished with exit code " + exitCode;
}
else {
message = null;
}
}
else {
if (!isStderrSuppressed()) {
GitVcs.getInstance(myProject).showErrorMessages(message);
}
}
if (message != null) {
//noinspection ThrowableInstanceNeverThrown
VcsException e = myException.getAndSet(new VcsException(message));
if (e != null) {
LOG.warn("Dropping previous exception: ", e);
}
}
}
}
@Override
public void startFailed(Throwable exception) {
//noinspection ThrowableInstanceNeverThrown
VcsException e = myException.getAndSet(new VcsException("Start failed: " + exception.getMessage(), exception));
if (e != null) {
LOG.warn("Dropping previous exception: ", e);
}
}
});
GitHandlerUtil.runInCurrentThread(this, null);
//noinspection ThrowableResultOfMethodCallIgnored
if (myException.get() != null) {
throw myException.get();
}
return myStdout.toByteArray();
}
}
@@ -73,7 +73,7 @@ public abstract class GitHandler {
/**
* the context project (might be a default project)
*/
private final Project myProject;
final Project myProject;
/**
* The descriptor for the command to be executed
*/