Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Valenta2002-03-19 10:13:17 -0500
committerMichael Valenta2002-03-19 10:13:17 -0500
commit42f0ba306b8dfa5cf05828f2dad523fd1cd279f5 (patch)
treeb9e758fa0467e83805671fb0edf8edc2d3a209fe
parent858209ad2172cae7bcd5d05719e5c43ed1457464 (diff)
downloadeclipse.platform.team-branch-11185.tar.gz
eclipse.platform.team-branch-11185.tar.xz
eclipse.platform.team-branch-11185.zip
Initial work for bug 11185branch-11185
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ICVSFile.java21
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/FileInputStreamWrapper.java188
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java15
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java69
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java38
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileWriter.java10
6 files changed, 256 insertions, 85 deletions
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ICVSFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ICVSFile.java
index b6be92849..ec87121e4 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ICVSFile.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ICVSFile.java
@@ -38,10 +38,15 @@ public interface ICVSFile extends ICVSResource {
* It is the responsibility of the caller to close the stream when finished.
*/
InputStream getInputStream() throws CVSException;
-
- /**
- * Gets an output stream for writing to the file.
+
+ /**
+ * Gets an appending output stream for writing to the file.
* It is the responsibility of the caller to close the stream when finished.
+ */
+ OutputStream getAppendingOutputStream() throws CVSException;
+
+ /**
+ * Set the contents of the file to the contents of the provided input stream
*
* @param responseType the type of reponse that was received from the server
*
@@ -49,14 +54,8 @@ public interface ICVSFile extends ICVSResource {
* MERGED - merging remote changes with local changes. Failure could result in loss of local changes
* CREATED - contents for a file that doesn't exist locally
* UPDATE_EXISTING - Replacing a local file with no local changes with remote changes.
- */
- OutputStream getOutputStream(int responseType, boolean keepLocalHistory) throws CVSException;
-
- /**
- * Gets an appending output stream for writing to the file.
- * It is the responsibility of the caller to close the stream when finished.
- */
- OutputStream getAppendingOutputStream() throws CVSException;
+ */
+ public void setContents(InputStream stream, int responseType, boolean keepLocalHistory, IProgressMonitor monitor) throws CVSException;
/**
* Sets the file's read-only permission.
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/FileInputStreamWrapper.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/FileInputStreamWrapper.java
new file mode 100644
index 000000000..c7aa32cc9
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/FileInputStreamWrapper.java
@@ -0,0 +1,188 @@
+package org.eclipse.team.internal.ccvs.core.client;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2002.
+ * All Rights Reserved.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+
+/**
+ * This class can be used to transfer a file from the CVS server to a local IFile
+ */
+public class FileInputStreamWrapper {
+
+ // default file transfer buffer size (in bytes)
+ private static int TRANSFER_BUFFER_SIZE = 8192;
+ // update progress bar in increments of this size (in bytes)
+ // no incremental progress shown for files smaller than this size
+ private static int TRANSFER_PROGRESS_INCREMENT = 32768;
+
+ // the platform's line termination sequence
+ private static final byte[] PLATFORM_NEWLINE_BYTES =
+ System.getProperty("line.separator").getBytes(); //$NON-NLS-1$ // at least one byte long
+ // the server's line termination sequence
+ private static final int SERVER_NEWLINE_BYTE = 0x0a; // exactly one byte long
+ private static final byte[] SERVER_NEWLINE_BYTES = new byte[] { SERVER_NEWLINE_BYTE };
+ // true iff newlines must be converted between platform and server formats
+ private static boolean DONT_CONVERT_NEWLINES = PLATFORM_NEWLINE_BYTES.length == 1
+ && PLATFORM_NEWLINE_BYTES[0] == SERVER_NEWLINE_BYTE;
+
+ // VCM 1.0 comitted files using CR/LF as a delimiter
+ private static final int CARRIAGE_RETURN_BYTE = 0x0d;
+
+ private InputStream input;
+ private long fileSize;
+ private int totalRead;
+ private boolean isBinary;
+ private IProgressMonitor monitor;
+ private byte[] buffer;
+ private int nextProgressThresh;
+
+ private static final byte[] BUFFER = new byte[TRANSFER_BUFFER_SIZE / 2];
+ private static final byte[] EXPANSION_BUFFER = new byte[TRANSFER_BUFFER_SIZE];
+
+ private int position;
+ private int bufferLength;
+ private String title;
+
+ public FileInputStreamWrapper(InputStream input, long fileSize, boolean isBinary, String title, IProgressMonitor monitor) {
+ this.input = input;
+ this.fileSize = fileSize;
+ this.totalRead = 0;
+ this.isBinary = isBinary;
+ this.monitor = monitor;
+ this.buffer = BUFFER;
+ this.nextProgressThresh = TRANSFER_PROGRESS_INCREMENT;
+ this.title = title;
+ }
+
+ public class InputStreamFromServer extends InputStream {
+ public int read() throws IOException {
+ if (position >= bufferLength) {
+ if (fill() == -1)
+ return -1;
+ }
+ return buffer[position++];
+ }
+ public int read(byte[] bytes) throws IOException {
+ return read(bytes, 0, bytes.length);
+ }
+ public int read(byte[] bytes, int offset, int length) throws IOException {
+ if (position >= bufferLength) {
+ if (fill() == -1)
+ return -1;
+ }
+ length = Math.min(bufferLength - position, length);
+ System.arraycopy(buffer, position, bytes, offset, length);
+ position += length;
+ return length;
+ }
+ }
+
+ /**
+ * Return a stream that can be passed to IFile#setContent()
+ * After the call to setContent, the receiver's input stream will be at the byte
+ * after the received file.
+ */
+ public InputStream getInputStream() {
+ return new InputStreamFromServer();
+ }
+
+ /*
+ * Transfers a file to or from the remove CVS server, possibly expanding line delimiters.
+ * <p>
+ * Line termination sequences are only converted upon request by specifying an
+ * array containing the expected sequence of bytes representing an outbound newline,
+ * and a single byte representing an inbound newline. If null is passed for the
+ * former, the file is assumed to have binary contents, hence no translation is
+ * performed.
+ * </p><p>
+ * Translation is performed on-the-fly, so the file need not fit in available memory.
+ * </p>
+ * @param in the input stream
+ * @param out the output stream
+ * @param size the source file size
+ * @param newlineIn the single byte for a received newline, ignored if binary
+ * @param newlineOut the sequence of bytes for sent newline, or null if binary
+ * @param monitor the progress monitor
+ * @param title the name of the file being received (as shown in the monitor)
+ */
+ private int fill() throws IOException {
+
+ // Check if we've read the entire file
+ if (totalRead == fileSize) {
+ return -1;
+ } else if (position < bufferLength) {
+ return bufferLength - position;
+ }
+
+ position = 0;
+
+ // If we're not converting, use the big buffer to read
+ if (isBinary || DONT_CONVERT_NEWLINES) {
+ buffer = EXPANSION_BUFFER;
+ } else {
+ buffer = BUFFER;
+ }
+
+ bufferLength = input.read(buffer, 0, (int) Math.min(buffer.length, fileSize - totalRead));
+ if (bufferLength == -1) {
+ // Unexpected end of stream
+ throw new IOException(Policy.bind("Session.readError")); //$NON-NLS-1$
+ }
+ totalRead += bufferLength;
+
+ if (isBinary || DONT_CONVERT_NEWLINES) {
+ return bufferLength;
+ }
+
+ bufferLength = convertNewLines(BUFFER, EXPANSION_BUFFER, bufferLength);
+ buffer = EXPANSION_BUFFER;
+
+ // update progress monitor
+ if (totalRead > nextProgressThresh) {
+ monitor.subTask(Policy.bind("Session.transfer", //$NON-NLS-1$
+ new Object[] { title, new Long(totalRead / 1024), new Long(fileSize / 1024)}));
+ nextProgressThresh = totalRead + TRANSFER_PROGRESS_INCREMENT;
+ }
+
+ return bufferLength;
+ }
+
+ /*
+ * Copy the bytes from the source to the target, converting any LF to the platform newline byte.
+ *
+ * There is special handling that will skip incoming CRs that precede LF.
+ */
+ private int convertNewLines(byte[] source, byte[] target, int length) {
+ boolean seenCR = false;
+ int targetPosition = 0;
+ for (int sourcePosition = 0; sourcePosition < length; ++sourcePosition) {
+ final byte b = source[sourcePosition];
+ if (b == CARRIAGE_RETURN_BYTE) {
+ // We keep track of CRs to perform autocorrection for improperly stored text files
+ seenCR = true;
+ } else {
+ if (b == SERVER_NEWLINE_BYTE) {
+ // if fixCRLF we ignore previous CR (if there was one)
+ // replace newlineIn with newlineOut
+ for (int x = 0; x < PLATFORM_NEWLINE_BYTES.length; ++x) target[targetPosition++] = PLATFORM_NEWLINE_BYTES[x];
+ } else {
+ if (seenCR) target[targetPosition++] = CARRIAGE_RETURN_BYTE; // preserve stray CR's
+ target[targetPosition++] = b;
+ }
+ seenCR = false;
+ }
+ }
+ if (seenCR) target[targetPosition++] = CARRIAGE_RETURN_BYTE;
+
+ return targetPosition;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java
index 7541bd044..9c2aa41d9 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Session.java
@@ -625,18 +625,9 @@ public class Session {
} catch (NumberFormatException e) {
throw new CVSException(Policy.bind("Session.badInt"), e); //$NON-NLS-1$
}
- // obtain an output stream for the file
- OutputStream out = file.getOutputStream(responseType, true);
- try {
- transferWithProgress(connection.getInputStream(), out, size, SERVER_NEWLINE_BYTE,
- isBinary ? null : PLATFORM_NEWLINE_BYTES, monitor, title);
- } finally {
- try {
- out.close();
- } catch (IOException e) {
- throw CVSException.wrapException(e);
- }
- }
+ // Set the contents of the file using the stream wrapper
+ FileInputStreamWrapper wrapper = new FileInputStreamWrapper(connection.getInputStream(), size, isBinary, title, monitor);
+ file.setContents(wrapper.getInputStream(), responseType, true, monitor);
}
/**
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java
index c541f3538..e89fecbde 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/EclipseFile.java
@@ -38,6 +38,8 @@ class EclipseFile extends EclipseResource implements ICVSFile {
private static final String TEMP_FILE_EXTENSION = ".tmp";//$NON-NLS-1$
+ private static final IPath PROJECT_META_DATA_PATH = new Path(".project");
+
/**
* Create a handle based on the given local resource.
*/
@@ -68,40 +70,6 @@ class EclipseFile extends EclipseResource implements ICVSFile {
}
}
- public OutputStream getOutputStream(final int responseType, final boolean keepLocalHistory) throws CVSException {
- return new ByteArrayOutputStream() {
- public void close() throws IOException {
- try {
- IFile file = getIFile();
- if (responseType == CREATED || (responseType == UPDATED && ! resource.exists())) {
- if (resource.exists()) {
- // Special handling for the .project meta-file
- // XXX This behavior should be restricted to the meta file!
- file.setContents(new ByteArrayInputStream(toByteArray()), true /*force*/, true /*keep history*/, null);
- } else {
- file.create(new ByteArrayInputStream(toByteArray()), false /*force*/, null);
- }
- } else if(responseType == UPDATE_EXISTING) {
- file.setContents(new ByteArrayInputStream(toByteArray()), false /*force*/, keepLocalHistory /*keep history*/, null);
- } else {
-
- file.setContents(new ByteArrayInputStream(toByteArray()), false /*force*/, keepLocalHistory /*keep history*/, null);
-
-// // Ensure we don't leave the file in a partially written state
-// IFile tempFile = file.getParent().getFile(new Path(file.getName() + TEMP_FILE_EXTENSION));
-// tempFile.create(new ByteArrayInputStream(toByteArray()), true /*force*/, null);
-// file.delete(false, true, null);
-// tempFile.move(new Path(file.getName()), true, true, null);
- }
- } catch(CoreException e) {
- throw new IOException(Policy.bind("EclipseFile_Problem_creating_resource", e.getMessage())); //$NON-NLS-1$ //$NON-NLS-2$
- } finally {
- super.close();
- }
- }
- };
- }
-
/*
* @see ICVSFile#getAppendingOutputStream()
*/
@@ -214,7 +182,40 @@ class EclipseFile extends EclipseResource implements ICVSFile {
public String getRemoteLocation(ICVSFolder stopSearching) throws CVSException {
return getParent().getRemoteLocation(stopSearching) + SEPARATOR + getName();
}
+
+ /*
+ * @see ICVSFile#setReadOnly()
+ */
+ public void setContents(InputStream stream, int responseType, boolean keepLocalHistory, IProgressMonitor monitor) throws CVSException {
+ try {
+ IFile file = getIFile();
+ if (responseType == CREATED || (responseType == UPDATED && ! resource.exists())) {
+ if (resource.exists()) {
+ if (PROJECT_META_DATA_PATH.equals(file.getFullPath().removeFirstSegments(1))) {
+ // Special handling for the .project meta-file
+ file.setContents(stream, true /*force*/, true /*keep history*/, monitor);
+ } else {
+ throw new CVSException("File " + file.getFullPath() + " already exists.");
+ }
+ } else {
+ file.create(stream, false /*force*/, null);
+ }
+ } else if(responseType == UPDATE_EXISTING) {
+ file.setContents(stream, false /*force*/, keepLocalHistory /*keep history*/, monitor);
+ } else {
+ file.setContents(stream, false /*force*/, keepLocalHistory /*keep history*/, monitor);
+ // // Ensure we don't leave the file in a partially written state
+ // IFile tempFile = file.getParent().getFile(new Path(file.getName() + TEMP_FILE_EXTENSION));
+ // tempFile.create(new ByteArrayInputStream(toByteArray()), true /*force*/, null);
+ // file.delete(false, true, null);
+ // tempFile.move(new Path(file.getName()), true, true, null);
+ }
+ } catch(CoreException e) {
+ throw new CVSException(Policy.bind("EclipseFile_Problem_creating_resource"), e); //$NON-NLS-1$
+ }
+ }
+
/*
* @see ICVSFile#setReadOnly()
*/
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
index 4eb3b5ed4..094117225 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
@@ -89,17 +89,7 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile, ICVSFi
// use the contents of the file on disk so that the server can calculate the relative
// sync state. This is a trick to allow the server to calculate sync state for us.
InputStream is = managed.getInputStream();
- OutputStream os = file.getOutputStream(ICVSFile.UPDATED, false);
- try {
- FileUtil.transfer(is, os);
- } catch(IOException e) {
- } finally {
- try {
- os.close();
- is.close();
- } catch(IOException e) {
- }
- }
+ file.setContents(is, ICVSFile.UPDATED, false, Policy.monitorFor(null));
parent.setChildren(new ICVSRemoteResource[] {file});
if( ! file.updateRevision(tag, monitor)) {
@@ -313,20 +303,24 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile, ICVSFi
public InputStream getInputStream() throws CVSException {
return new ByteArrayInputStream(contents == null ? new byte[0] : contents);
}
-
+
/*
- * @see ICVSFile#getOutputStream()
+ * @see ICVSFile#setReadOnly()
*/
- public OutputStream getOutputStream(int responseType, boolean keepLocalHistory) throws CVSException {
- // stores the contents of the file when the stream is closed
- // could perhaps be optimized in some manner to avoid excessive array copying
- return new ByteArrayOutputStream() {
- public void close() throws IOException {
- contents = toByteArray();
- super.close();
+ public void setContents(InputStream stream, int responseType, boolean keepLocalHistory, IProgressMonitor monitor) throws CVSException {
+ try {
+ byte[] buffer = new byte[1024];
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int read;
+ while ((read = stream.read(buffer)) >= 0) {
+ Policy.checkCanceled(monitor);
+ out.write(buffer, 0, read);
}
- };
- }
+ contents = out.toByteArray();
+ } catch(IOException e) {
+ throw new CVSException(Policy.bind("")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
public void setReadOnly(boolean readOnly) throws CVSException {
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileWriter.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileWriter.java
index 3142fa235..19cb87e24 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileWriter.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/SyncFileWriter.java
@@ -7,6 +7,8 @@ package org.eclipse.team.internal.ccvs.core.util;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
@@ -15,14 +17,9 @@ import java.util.List;
import java.util.Map;
import java.util.TreeMap;
-import org.eclipse.core.resources.IContainer;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Path;
import org.eclipse.team.ccvs.core.CVSTag;
import org.eclipse.team.ccvs.core.ICVSFile;
import org.eclipse.team.ccvs.core.ICVSFolder;
-import org.eclipse.team.ccvs.core.ICVSResource;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.resources.CVSEntryLineTag;
@@ -283,8 +280,9 @@ public class SyncFileWriter {
* compatibility with other CVS clients.
*/
private static void writeLines(ICVSFile file, String[] contents) throws CVSException {
- OutputStream os = new BufferedOutputStream(file.getOutputStream(ICVSFile.UPDATED, false));
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
writeLinesToStreamAndClose(os, contents);
+ file.setContents(new ByteArrayInputStream(os.toByteArray()), ICVSFile.UPDATED, false, Policy.monitorFor(null));
}
private static void writeLinesToStreamAndClose(OutputStream os, String[] contents)

Back to the top