KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > core > client > Session


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Red Hat Incorporated - is/setExecutable() code
11  *******************************************************************************/

12 package org.eclipse.team.internal.ccvs.core.client;
13
14 import java.io.*;
15 import java.util.ArrayList JavaDoc;
16 import java.util.Arrays JavaDoc;
17 import java.util.Collection JavaDoc;
18 import java.util.Date JavaDoc;
19 import java.util.Iterator JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Map JavaDoc;
22
23 import java.util.zip.GZIPInputStream JavaDoc;
24 import java.util.zip.GZIPOutputStream JavaDoc;
25
26 import org.eclipse.core.resources.IResource;
27 import org.eclipse.core.runtime.*;
28 import org.eclipse.osgi.util.NLS;
29 import org.eclipse.team.core.RepositoryProvider;
30 import org.eclipse.team.internal.ccvs.core.*;
31 import org.eclipse.team.internal.ccvs.core.client.Command.GlobalOption;
32 import org.eclipse.team.internal.ccvs.core.client.Command.QuietOption;
33 import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
34 import org.eclipse.team.internal.ccvs.core.connection.Connection;
35 import org.eclipse.team.internal.ccvs.core.syncinfo.NotifyInfo;
36 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
37 import org.eclipse.team.internal.ccvs.core.util.Util;
38 import org.eclipse.team.internal.core.streams.*;
39
40 /**
41  * Maintains CVS communication state for the lifetime of a connection
42  * to a remote repository. This class covers the initialization, use,
43  * and eventual shutdown of a dialogue between a CVS client and a
44  * remote server. This dialogue may be monitored through the use of
45  * a console.
46  *
47  * Initially the Session is in a CLOSED state during which communication
48  * with the server cannot take place. Once OPENED, any number of commands
49  * may be issued serially to the server, one at a time. When finished, the
50  * Session MUST be CLOSED once again to prevent eventual local and/or
51  * remote resource exhaustion. The session can either be discarded, or
52  * re-opened for use with the same server though no state is persisted from
53  * previous connections except for console attributes.
54  *
55  * CVSExceptions are thrown only as a result of unrecoverable errors. Once
56  * this happens, commands must no longer be issued to the server. If the
57  * Session is in the OPEN state, it is still the responsibility of the
58  * caller to CLOSE it before moving on.
59  */

60 public class Session {
61     public static final String JavaDoc CURRENT_LOCAL_FOLDER = "."; //$NON-NLS-1$
62
public static final String JavaDoc CURRENT_REMOTE_FOLDER = ""; //$NON-NLS-1$
63
public static final String JavaDoc SERVER_SEPARATOR = "/"; //$NON-NLS-1$
64

65     // default file transfer buffer size (in bytes)
66
private static final int TRANSFER_BUFFER_SIZE = 8192;
67     // update progress bar in increments of this size (in bytes)
68
// no incremental progress shown for files smaller than this size
69
private static final int TRANSFER_PROGRESS_INCREMENT = 32768;
70
71     public static final boolean IS_CRLF_PLATFORM = Arrays.equals(
72         System.getProperty("line.separator").getBytes(), new byte[] { '\r', '\n' }); //$NON-NLS-1$
73

74     private CVSRepositoryLocation location;
75     private ICVSFolder localRoot;
76     private boolean outputToConsole;
77     private Connection connection = null;
78     private String JavaDoc validRequests = null;
79     private Date JavaDoc modTime = null;
80     private boolean noLocalChanges = false;
81     private boolean createBackups = true;
82     private int compressionLevel = 0;
83     private List JavaDoc expansions;
84     private Collection JavaDoc /* of ICVSFile */ textTransferOverrideSet = null;
85     
86     // state need to indicate whether
87
private boolean ignoringLocalChanges = false;
88
89     // The resource bundle key that provides the file sending message
90
private String JavaDoc sendFileTitleMessage;
91     private Map JavaDoc responseHandlers;
92     
93     // List of errors accumulated while the command is executing
94
private List JavaDoc errors = new ArrayList JavaDoc();
95     
96     private Command currentCommand;
97
98     /**
99      * Creates a new CVS session, initially in the CLOSED state.
100      * By default, command output is directed to the console.
101      *
102      * @param location the CVS repository location used for this session
103      * @param localRoot represents the current working directory of the client
104      */

105     public Session(ICVSRepositoryLocation location, ICVSFolder localRoot) {
106         this(location, localRoot, true);
107     }
108     
109     /**
110      * Creates a new CVS session, initially in the CLOSED state.
111      *
112      * @param location the CVS repository location used for this session
113      * @param localRoot represents the current working directory of the client
114      * @param outputToConsole if true, command output is directed to the console
115      */

116     public Session(ICVSRepositoryLocation location, ICVSFolder localRoot, boolean outputToConsole) {
117         this.location = (CVSRepositoryLocation) location;
118         this.localRoot = localRoot;
119         this.outputToConsole = outputToConsole;
120     }
121     
122     /*
123      * Add a module expansion receivered from the server.
124      * This is only used by the ModuleExpansionsHandler
125      */

126     protected void addModuleExpansion(String JavaDoc expansion) {
127         expansions.add(expansion);
128     }
129     
130     /*
131      * Add a module expansion receivered from the server.
132      * This is only used by the ExpandModules command
133      */

134     protected void resetModuleExpansion() {
135         if (expansions == null)
136             expansions = new ArrayList JavaDoc();
137         else
138             expansions.clear();
139     }
140     
141     /**
142      * Opens, authenticates and initializes a connection to the server specified
143      * for the remote location.
144      *
145      * @param monitor the progress monitor
146      * @throws IllegalStateException if the Session is not in the CLOSED state
147      */

148     public void open(IProgressMonitor monitor) throws CVSException {
149         open(monitor, true /* write access*/);
150     }
151     
152     public void open(IProgressMonitor monitor, boolean writeAccess) throws CVSException {
153         if (connection != null) throw new IllegalStateException JavaDoc();
154         monitor = Policy.monitorFor(monitor);
155         monitor.beginTask(null, 100);
156         boolean opened = false;
157     
158         try {
159             connection = getLocationForConnection(writeAccess).openConnection(Policy.subMonitorFor(monitor, 50));
160             
161             // If we're connected to a CVSNT server or we don't know the platform,
162
// accept MT. Otherwise don't.
163
boolean useMT = ! (location.getServerPlatform() == CVSRepositoryLocation.CVS_SERVER);
164             if ( ! useMT) {
165                 removeResponseHandler("MT"); //$NON-NLS-1$
166
}
167             
168             // tell the server the names of the responses we can handle
169
connection.writeLine("Valid-responses " + makeResponseList()); //$NON-NLS-1$
170
// Flush in order to recieve the valid requests
171
connection.flush();
172     
173             // ask for the set of valid requests
174
IStatus status = Request.VALID_REQUESTS.execute(this, Policy.subMonitorFor(monitor, 40));
175             if (!status.isOK()) {
176                 throw new CVSException(status);
177             }
178             
179             // set the root directory on the server for this connection
180
connection.writeLine("Root " + getRepositoryRoot()); //$NON-NLS-1$
181

182             // enable compression
183
compressionLevel = CVSProviderPlugin.getPlugin().getCompressionLevel();
184             if (compressionLevel != 0 && isValidRequest("gzip-file-contents")) { //$NON-NLS-1$
185
// Enable the use of CVS 1.8 per-file compression mechanism.
186
// The newer Gzip-stream request seems to be problematic due to Java's
187
// GZIPInputStream tendency to block on read() rather than to return a
188
// partially filled buffer. The latter option would be better since it
189
// can make more effective use of the code dictionary, if it can be made
190
// to work...
191
connection.writeLine("gzip-file-contents " + Integer.toString(compressionLevel)); //$NON-NLS-1$
192
} else {
193                 compressionLevel = 0;
194             }
195             
196             // get the server platform if it is unknown
197
if (CVSProviderPlugin.getPlugin().isDetermineVersionEnabled() && location.getServerPlatform() == CVSRepositoryLocation.UNDETERMINED_PLATFORM) {
198                 Command.VERSION.execute(this, location, Policy.subMonitorFor(monitor, 10));
199             }
200             opened = true;
201         } finally {
202             if (connection != null && ! opened) {
203                 close();
204             }
205             monitor.done();
206         }
207     }
208     
209     /*
210      * Return the location to be used for this connection
211      */

212     private CVSRepositoryLocation getLocationForConnection(boolean writeAccess) {
213         return location;
214     }
215
216     /**
217      * Closes a connection to the server.
218      *
219      * @throws IllegalStateException if the Session is not in the OPEN state
220      */

221     public void close() {
222         if (connection != null) {
223             connection.close();
224             connection = null;
225             validRequests = null;
226         }
227     }
228     
229     /**
230      * Determines if the server supports the specified request.
231      *
232      * @param request the request string to verify
233      * @return true iff the request is supported
234      */

235     public boolean isValidRequest(String JavaDoc request) {
236         return (validRequests == null) ||
237             (validRequests.indexOf(" " + request + " ") != -1); //$NON-NLS-1$ //$NON-NLS-2$
238
}
239     
240     public boolean isCVSNT() {
241         if (location.getServerPlatform() == CVSRepositoryLocation.UNDETERMINED_PLATFORM) {
242             return location.getRootDirectory().indexOf(':') == 1;
243         } else {
244             return location.getServerPlatform() == CVSRepositoryLocation.CVSNT_SERVER;
245         }
246     }
247     
248     /**
249      * Returns the local root folder for this session.
250      * <p>
251      * Generally speaking, specifies the "current working directory" at
252      * the time of invocation of an equivalent CVS command-line client.
253      * </p>
254      *
255      * @return the local root folder
256      */

257     public ICVSFolder getLocalRoot() {
258         return localRoot;
259     }
260
261     /**
262      * Return the list of module expansions communicated from the server.
263      *
264      * The modules expansions are typically a directory path of length 1
265      * but can be of greater length on occasion.
266      */

267     public String JavaDoc[] getModuleExpansions() {
268         if (expansions == null) return new String JavaDoc[0];
269         return (String JavaDoc[]) expansions.toArray(new String JavaDoc[expansions.size()]);
270     }
271     
272     /**
273      * Returns the repository root folder for this session.
274      * <p>
275      * Specifies the unqualified path to the CVS repository root folder
276      * on the server.
277      * </p>
278      *
279      * @return the repository root folder
280      */

281     public String JavaDoc getRepositoryRoot() {
282         return location.getRootDirectory();
283     }
284     
285     /**
286      * Returns an object representing the CVS repository location for this session.
287      *
288      * @return the CVS repository location
289      */

290     public ICVSRepositoryLocation getCVSRepositoryLocation() {
291         return location;
292     }
293     
294     /**
295      * Receives a line of text minus the newline from the server.
296      *
297      * @return the line of text
298      */

299     public String JavaDoc readLine() throws CVSException {
300         return connection.readLine();
301     }
302
303     /**
304      * Sends a line of text followed by a newline to the server.
305      *
306      * @param line the line of text
307      */

308     public void writeLine(String JavaDoc line) throws CVSException {
309         connection.writeLine(line);
310     }
311
312     /**
313      * Sends an argument to the server.
314      * <p>e.g. sendArgument("Hello\nWorld\n Hello World") sends:
315      * <pre>
316      * Argument Hello \n
317      * Argumentx World \n
318      * Argumentx Hello World \n
319      * </pre></p>
320      *
321      * @param arg the argument to send
322      */

323     public void sendArgument(String JavaDoc arg) throws CVSException {
324         connection.write("Argument "); //$NON-NLS-1$
325
int oldPos = 0;
326         for (;;) {
327             int pos = arg.indexOf('\n', oldPos);
328             if (pos == -1) break;
329             connection.writeLine(stripTrainingCR(arg.substring(oldPos, pos)));
330             connection.write("Argumentx "); //$NON-NLS-1$
331
oldPos = pos + 1;
332         }
333         connection.writeLine(stripTrainingCR(arg.substring(oldPos)));
334     }
335
336     /*
337      * Remove any trailing CR from the string
338      */

339     private String JavaDoc stripTrainingCR(String JavaDoc string) {
340         if (string.endsWith("\r")) { //$NON-NLS-1$
341
return string.substring(0, string.length() - 1);
342         }
343         return string;
344     }
345
346     /**
347      * Sends a request to the server and flushes any output buffers.
348      *
349      * @param requestId the string associated with the request to be executed
350      */

351     public void sendRequest(String JavaDoc requestId) throws CVSException {
352         connection.writeLine(requestId);
353         connection.flush();
354     }
355
356     /**
357      * Sends an Is-modified request to the server without the file contents.
358      * <p>e.g. if a file called "local_file" was modified, sends:
359      * <pre>
360      * Is-modified local_file \n
361      * </pre></p><p>
362      * This request is an optimized form of the Modified request and may not
363      * be supported by all servers. Hence, if it is not supported, a Modified
364      * request is sent instead along with the file's contents. According to
365      * the CVS protocol specification, this request is only safe for use with
366      * some forms of: admin, annotate, diff, editors, log, watch-add, watch-off,
367      * watch-on, watch-remove, and watchers.<br>
368      * It may be possible to use this for: add, export, remove and status.<br>
369      * Do not use with co, ci, history, init, import, release, rdiff, rtag, or update.
370      * </p><p>
371      * Note: The most recent Directory request must have specified the file's
372      * parent folder.
373      * </p>
374      *
375      * @param file the file that was modified
376      * @see #sendModified
377      */

378     public void sendIsModified(ICVSFile file, boolean isBinary, IProgressMonitor monitor)
379         throws CVSException {
380         if (isValidRequest("Is-modified")) { //$NON-NLS-1$
381
connection.writeLine("Is-modified " + file.getName()); //$NON-NLS-1$
382
} else {
383             sendModified(file, isBinary, monitor);
384         }
385     }
386
387     /**
388      * Sends a Static-directory request to the server.
389      * <p>
390      * Indicates that the directory specified in the most recent Directory request
391      * is static. No new files will be checked out into this directory unless
392      * explicitly requested.
393      * </p>
394      */

395     public void sendStaticDirectory() throws CVSException {
396         connection.writeLine("Static-directory"); //$NON-NLS-1$
397
}
398
399     /**
400      * Sends a Directory request to the server with a constructed path.
401      * <p>
402      * It may be necessary at times to guess the remote path of a directory since
403      * it does not exist yet. In this case we construct a remote path based on the
404      * local path by prepending the local path with the repository root. This may
405      * not work in the presence of modules, so only use it for creating new projects.
406      * </p><p>
407      * Note: A CVS repository root can end with a trailing slash. The CVS server
408      * expects that the repository root sent contain this extra slash. Including
409      * the foward slash in addition to the absolute remote path makes for a string
410      * containing two consecutive slashes (e.g. /home/cvs/repo//projecta/a.txt).
411      * This is valid in the CVS protocol.
412      * </p>
413      */

414     public void sendConstructedDirectory(String JavaDoc localDir) throws CVSException {
415         String JavaDoc path = Util.appendPath(getRepositoryRoot(), localDir);
416         sendDirectory(localDir, path);
417     }
418
419     /**
420      * Sends a Directory request to the server.
421      * <p>e.g. sendDirectory("local_dir", "remote_dir") sends:
422      * <pre>
423      * Directory local_dir
424      * repository_root/remote_dir
425      * </pre></p>
426      *
427      * @param localDir the path of the local directory relative to localRoot
428      * @param remoteDir the path of the remote directory relative to repositoryRoot
429      */

430     public void sendDirectory(String JavaDoc localDir, String JavaDoc remoteDir) throws CVSException {
431         if (localDir.length() == 0) localDir = CURRENT_LOCAL_FOLDER;
432         connection.writeLine("Directory " + localDir); //$NON-NLS-1$
433
connection.writeLine(remoteDir);
434     }
435
436     /**
437      * Sends a Directory request for the localRoot.
438      */

439     public void sendLocalRootDirectory() throws CVSException {
440         sendDirectory(CURRENT_LOCAL_FOLDER, localRoot.getRemoteLocation(localRoot));
441     }
442
443     /**
444      * Sends a Directory request for the localRoot with a constructed path.
445      * <p>
446      * Use this when creating a new project that does not exist in the repository.
447      * </p>
448      * @see #sendConstructedDirectory
449      */

450     public void sendConstructedRootDirectory() throws CVSException {
451         sendConstructedDirectory(""); //$NON-NLS-1$
452
}
453
454     /**
455      * Sends an Entry request to the server.
456      * <p>
457      * Indicates that a file is managed (but it may not exist locally). Sends
458      * the file's entry line to the server to indicate the version that was
459      * previously checked out.
460      * </p><p>
461      * Note: The most recent Directory request must have specified the file's
462      * parent folder.
463      * </p>
464      *
465      * @param entryLine the formatted entry line of the managed file.
466      */

467     public void sendEntry(byte[] syncBytes, String JavaDoc serverTimestamp) throws CVSException {
468         connection.write("Entry "); //$NON-NLS-1$
469
if (serverTimestamp == null) {
470             serverTimestamp = ""; //$NON-NLS-1$
471
}
472         int start = Util.getOffsetOfDelimeter(syncBytes, (byte)'/', 0, 3);
473         if (start == -1) {
474             // something is wrong with the entry line so just send it as is
475
// and let the server report the error.
476
connection.writeLine(new String JavaDoc(syncBytes));
477             return;
478         }
479         int end = Util.getOffsetOfDelimeter(syncBytes, (byte)'/', start + 1, 1);
480         if (end == -1) {
481             // something is wrong with the entry line so just send it as is
482
// and let the server report the error.
483
connection.writeLine(new String JavaDoc(syncBytes));
484             return;
485         }
486         connection.write(new String JavaDoc(syncBytes, 0, start + 1));
487         connection.write(serverTimestamp);
488         connection.writeLine(new String JavaDoc(syncBytes, end, syncBytes.length - end));
489     }
490
491     /**
492      * Sends a global options to the server.
493      * <p>e.g. sendGlobalOption("-n") sends:
494      * <pre>
495      * Global_option -n \n
496      * </pre></p>
497      *
498      * @param option the global option to send
499      */

500     public void sendGlobalOption(String JavaDoc option) throws CVSException {
501         connection.writeLine("Global_option " + option); //$NON-NLS-1$
502
}
503
504     /**
505      * Sends an Unchanged request to the server.
506      * <p>e.g. if a file called "local_file" was not modified, sends:
507      * <pre>
508      * Unchanged local_file \n
509      * </pre></p><p>
510      * Note: The most recent Directory request must have specified the file's
511      * parent folder.
512      * </p>
513      *
514      * @param file the file that was not modified
515      */

516     public void sendUnchanged(ICVSFile file) throws CVSException {
517         connection.writeLine("Unchanged " + file.getName()); //$NON-NLS-1$
518
}
519     
520     /**
521      * Sends the Notify request to the server
522      */

523     public void sendNotify(ICVSFolder parent, NotifyInfo info)
524         throws CVSException {
525         
526         String JavaDoc filename = info.getName();
527         connection.writeLine("Notify " + filename); //$NON-NLS-1$
528
connection.writeLine(info.getServerLine(parent));
529     }
530         
531     /**
532      * Sends a Questionable request to the server.
533      * <p>
534      * Indicates that a file exists locally but is unmanaged. Asks the server
535      * whether or not the file should be ignored in subsequent CVS operations.
536      * The reply to the request occurs in the form of special M-type message
537      * responses prefixed with '?' when the next command is executed.
538      * </p><p>
539      * Note: The most recent Directory request must have specified the file's
540      * parent folder.
541      * </p>
542      *
543      * @param resource the local file or folder
544      */

545     public void sendQuestionable(ICVSResource resource) throws CVSException {
546         connection.writeLine("Questionable " + resource.getName()); //$NON-NLS-1$
547
}
548
549     /**
550      * Sends a Sticky tag request to the server.
551      * <p>
552      * Indicates that the directory specified in the most recent Directory request
553      * has a sticky tag or date, and sends the tag's contents.
554      * </p>
555      *
556      * @param tag the sticky tag associated with the directory
557      */

558     public void sendSticky(String JavaDoc tag) throws CVSException {
559         connection.writeLine("Sticky " + tag); //$NON-NLS-1$
560
}
561
562     /**
563      * Sends a Modified request to the server along with the file contents.
564      * <p>e.g. if a file called "local_file" was modified, sends:
565      * <pre>
566      * Modified local_file \n
567      * file_permissions \n
568      * file_size \n
569      * [... file_contents ...]
570      * </pre></p><p>
571      * Under some circumstances, Is-modified may be used in place of this request.<br>
572      * Do not use with history, init, import, rdiff, release, rtag, or update.
573      * </p><p>
574      * Note: The most recent Directory request must have specified the file's
575      * parent folder.
576      * </p>
577      *
578      * @param file the file that was modified
579      * @param isBinary if true the file is sent without translating line delimiters
580      * @param monitor the progress monitor
581      * @see #sendIsModified
582      */

583     public void sendModified(ICVSFile file, boolean isBinary, IProgressMonitor monitor)
584         throws CVSException {
585             sendModified(file, isBinary, true, monitor);
586     }
587
588     public void sendModified(ICVSFile file, boolean isBinary, boolean sendBinary, IProgressMonitor monitor)
589                 throws CVSException {
590         
591         String JavaDoc filename = file.getName();
592         connection.writeLine("Modified " + filename); //$NON-NLS-1$
593
// send the default permissions for now
594
if (file.isExecutable()) {
595             connection.writeLine(ResourceSyncInfo.getDefaultExecutablePermissions());
596         } else {
597             connection.writeLine(ResourceSyncInfo.getDefaultPermissions());
598         }
599         sendFile(file, isBinary, sendBinary, monitor);
600     }
601     
602         /**
603          * Sends a file to the remote CVS server, possibly translating line delimiters.
604          * <p>
605          * Line termination sequences are automatically converted to linefeeds only
606          * (required by the CVS specification) when sending non-binary files. This
607          * may alter the actual size and contents of the file that is sent.
608          * </p><p>
609          * Note: Non-binary files must be small enough to fit in available memory.
610          * </p>
611          * @param file the file to be sent
612          * @param isBinary is true if the file should be sent without translation
613          * @param monitor the progress monitor
614          */

615         public void sendFile(ICVSStorage file, boolean isBinary, boolean sendBinary, IProgressMonitor monitor) throws CVSException {
616             // check overrides
617
if (textTransferOverrideSet != null &&
618                 textTransferOverrideSet.contains(file)) isBinary = false;
619     
620             // update progress monitor
621
final String JavaDoc title = NLS.bind(getSendFileTitleMessage(), (new Object JavaDoc[]{ Util.toTruncatedPath(file, localRoot, 3) }));
622             monitor.subTask(NLS.bind(CVSMessages.Session_transferNoSize, new String JavaDoc[] { title }));
623             try {
624                 InputStream JavaDoc in = null;
625                 long length;
626                 try {
627                     if (isBinary && !sendBinary) {
628                         byte[] bytes = "hello".getBytes(); //$NON-NLS-1$
629
sendUncompressedBytes(new ByteArrayInputStream(bytes), bytes.length);
630                         return;
631                     }
632                     
633                     if (compressionLevel == 0) {
634                         in = file.getContents();
635                         if (!isBinary && IS_CRLF_PLATFORM){
636                             // uncompressed text
637
byte[] buffer = new byte[TRANSFER_BUFFER_SIZE];
638                             in = new CRLFtoLFInputStream(in);
639                             ByteCountOutputStream counter = new ByteCountOutputStream();
640                             try {
641                                 for (int count; (count = in.read(buffer)) != -1;) counter.write(buffer, 0, count);
642                             } finally {
643                                 counter.close();
644                             }
645                             in.close();
646                             length = counter.getSize();
647                             in = new CRLFtoLFInputStream(file.getContents());
648                         } else {
649                             // uncompressed binary
650
length = file.getSize();
651                         }
652                         in = new ProgressMonitorInputStream(in, length, TRANSFER_PROGRESS_INCREMENT, monitor) {
653                             protected void updateMonitor(long bytesRead, long bytesTotal, IProgressMonitor monitor) {
654                                 if (bytesRead == 0) return;
655                                 Assert.isTrue(bytesRead <= bytesTotal);
656                                 monitor.subTask(NLS.bind(CVSMessages.Session_transfer, (new Object JavaDoc[] { title, Long.toString(bytesRead >> 10), Long.toString(bytesTotal >> 10) })));
657                             }
658                         };
659                         sendUncompressedBytes(in, length);
660                     } else {
661                         monitor.subTask(NLS.bind(CVSMessages.Session_calculatingCompressedSize, new String JavaDoc[] { Util.toTruncatedPath(file, localRoot, 3) }));
662                         in = file.getContents();
663                         byte[] buffer = new byte[TRANSFER_BUFFER_SIZE];
664                         ByteCountOutputStream counter = new ByteCountOutputStream();
665                         OutputStream JavaDoc zout = new GZIPOutputStream JavaDoc(counter);
666                         if (!isBinary && IS_CRLF_PLATFORM) in = new CRLFtoLFInputStream(in);
667                         try {
668                             for (int count; (count = in.read(buffer)) != -1;) zout.write(buffer, 0, count);
669                         } finally {
670                             zout.close();
671                         }
672                         in.close();
673                         in = file.getContents();
674                         in = new ProgressMonitorInputStream(in, file.getSize(), TRANSFER_PROGRESS_INCREMENT, monitor) {
675                             protected void updateMonitor(long bytesRead, long bytesTotal, IProgressMonitor monitor) {
676                                 if (bytesRead == 0) return;
677                                 Assert.isTrue(bytesRead <= bytesTotal);
678                                 monitor.subTask(NLS.bind(CVSMessages.Session_transfer, (new Object JavaDoc[] { title, Long.toString(bytesRead >> 10), Long.toString(bytesTotal >> 10) })));
679                             }
680                         };
681                         if (!isBinary && IS_CRLF_PLATFORM) in = new CRLFtoLFInputStream(in);
682                         sendCompressedBytes(in, counter.getSize());
683                     }
684                 } finally {
685                     if (in != null) in.close();
686                 }
687             } catch (IOException e) {
688                 throw CVSException.wrapException(e);
689             }
690         }
691
692     /*
693      * Send the contents of the input stream to CVS.
694      * Length must equal the number of bytes that will be transferred
695      * across the wire, that is, the compressed file size.
696      */

697     private void sendCompressedBytes(InputStream JavaDoc in, long length) throws IOException, CVSException {
698         String JavaDoc sizeLine = "z" + Long.toString(length); //$NON-NLS-1$
699
writeLine(sizeLine);
700         OutputStream JavaDoc out = connection.getOutputStream();
701         GZIPOutputStream JavaDoc zo = new GZIPOutputStream JavaDoc(out);
702         byte[] buffer = new byte[TRANSFER_BUFFER_SIZE];
703         for (int count;
704         (count = in.read(buffer)) != -1;)
705         zo.write(buffer, 0, count);
706         zo.finish();
707     }
708
709     /*
710      * Send the contents of the input stream to CVS.
711      * Length must equal the number of bytes that will be transferred
712      * across the wire.
713      */

714     private void sendUncompressedBytes(InputStream JavaDoc in, long length) throws IOException, CVSException {
715         OutputStream JavaDoc out = connection.getOutputStream();
716         String JavaDoc sizeLine = Long.toString(length);
717         writeLine(sizeLine);
718         byte[] buffer = new byte[TRANSFER_BUFFER_SIZE];
719         for (int count; (count = in.read(buffer)) != -1;) out.write(buffer, 0, count);
720     }
721
722
723
724
725     /**
726      * Receives a file from the remote CVS server, possibly translating line delimiters.
727      * <p>
728      * Line termination sequences are automatically converted to platform format
729      * only when receiving non-binary files. This may alter the actual size and
730      * contents of the file that is received.
731      * </p><p>
732      * Translation is performed on-the-fly, so the file need not fit in available memory.
733      * </p>
734      * @param file the file to be received
735      * @param isBinary is true if the file should be received without translation
736      * @param responseType one of the ICVSFile updated types (UPDATED, CREATED, MERGED, UPDATE_EXISTING)
737      * indicating what repsonse type provided the file contents
738      * @param monitor the progress monitor
739      */

740     public void receiveFile(ICVSStorage file, boolean isBinary, int responseType, IProgressMonitor monitor)
741     throws CVSException {
742         // check overrides
743
if (textTransferOverrideSet != null &&
744             textTransferOverrideSet.contains(file)) isBinary = false;
745
746         // update progress monitor
747
final String JavaDoc title = NLS.bind(CVSMessages.Session_receiving, (new Object JavaDoc[]{ Util.toTruncatedPath(file, localRoot, 3) }));
748         monitor.subTask(NLS.bind(CVSMessages.Session_transferNoSize, new String JavaDoc[] { title }));
749         // get the file size from the server
750
long size;
751         boolean compressed = false;
752         String JavaDoc sizeLine = null;
753         try {
754             sizeLine = readLine();
755             if (sizeLine.charAt(0) == 'z') {
756                 compressed = true;
757                 sizeLine = sizeLine.substring(1);
758             }
759             size = Long.parseLong(sizeLine, 10);
760         } catch (NumberFormatException JavaDoc e) {
761             // In some cases, the server will give us an error line here
762
if (sizeLine != null && sizeLine.startsWith("E")) { //$NON-NLS-1$
763
handleErrorLine(sizeLine.substring(1).trim(), org.eclipse.core.runtime.Status.OK_STATUS);
764                 return;
765             } else {
766                 IStatus status = new CVSStatus(IStatus.ERROR,CVSStatus.ERROR,CVSMessages.Session_badInt, e, localRoot);
767                 throw new CVSException(status);
768             }
769         }
770         // create an input stream that spans the next 'size' bytes from the connection
771
InputStream JavaDoc in = new SizeConstrainedInputStream(connection.getInputStream(), size, true /*discardOnClose*/);
772         // setup progress monitoring
773
in = new ProgressMonitorInputStream(in, size, TRANSFER_PROGRESS_INCREMENT, monitor) {
774             protected void updateMonitor(long bytesRead, long bytesTotal, IProgressMonitor monitor) {
775                 if (bytesRead == 0) return;
776                 monitor.subTask(NLS.bind(CVSMessages.Session_transfer, (new Object JavaDoc[] { title, Long.toString(bytesRead >> 10), Long.toString(bytesTotal >> 10) })));
777             }
778         };
779         // if compression enabled, decompress on the fly
780
if (compressed) {
781             try {
782                 in = new GZIPInputStream JavaDoc(in);
783             } catch (IOException e) {
784                 throw CVSException.wrapException(e);
785             }
786         }
787         // if not binary, translate line delimiters on the fly
788
if (! isBinary) {
789             // switch from LF to CRLF if appropriate
790
if (IS_CRLF_PLATFORM && CVSProviderPlugin.getPlugin().isUsePlatformLineend()) {
791                 // auto-correct for CRLF line-ends that come from the server
792
in = new CRLFtoLFInputStream(in);
793                 // convert LF to CRLF
794
in = new LFtoCRLFInputStream(in);
795             } else {
796                 // be nice and warn about text files that contain CRLF
797
in = new CRLFDetectInputStream(in, file);
798             }
799         }
800         // write the file locally
801
file.setContents(in, responseType, true, new NullProgressMonitor());
802     }
803
804     /**
805      * Stores the value of the last Mod-time response encountered.
806      * Valid only for the duration of a single CVS command.
807      */

808     void setModTime(Date JavaDoc modTime) {
809         this.modTime = modTime;
810     }
811     
812     /**
813      * Returns the stored value of the last Mod-time response,
814      * or null if there was none while processing the current command.
815      */

816     Date JavaDoc getModTime() {
817         return modTime;
818     }
819     
820     /**
821      * Stores true if the -n global option was specified for the current command.
822      * Valid only for the duration of a single CVS command.
823      */

824     void setNoLocalChanges(boolean noLocalChanges) {
825         this.noLocalChanges = noLocalChanges;
826     }
827     
828     /**
829      * Returns true if the -n global option was specified for the current command,
830      * false otherwise.
831      */

832     boolean isNoLocalChanges() {
833         return noLocalChanges;
834     }
835     
836     /**
837      * Callback hook for the ValidRequestsHandler to specify the set of valid
838      * requests for this session.
839      */

840     void setValidRequests(String JavaDoc validRequests) {
841         this.validRequests = " " + validRequests + " "; //$NON-NLS-1$ //$NON-NLS-2$
842
}
843
844     public boolean isOutputToConsole() {
845         return outputToConsole;
846     }
847
848     /**
849      * Stores a flag as to whether .# files will be created. (Default is true)
850      * @param createBackups if true, creates .# files at the server's request
851      */

852     void setCreateBackups(boolean createBackups) {
853         this.createBackups = createBackups;
854     }
855
856     /**
857      * Returns a flag as to whether .# files will be created.
858      */

859     boolean isCreateBackups() {
860         return createBackups;
861     }
862
863     /**
864      * Gets the sendFileTitleKey.
865      * @return Returns a String
866      */

867     String JavaDoc getSendFileTitleMessage() {
868         if (sendFileTitleMessage == null)
869             return CVSMessages.Session_sending;
870         return sendFileTitleMessage;
871     }
872
873     /**
874      * Sets the sendFileTitleKey.
875      * @param sendFileTitleKey The sendFileTitleKey to set
876      */

877     public void setSendFileTitleKey(String JavaDoc sendFileTitleMessage) {
878         this.sendFileTitleMessage = sendFileTitleMessage;
879     }
880     
881     /**
882      * Remembers a set of files that must be transferred as 'text'
883      * regardless of what the isBinary parameter to sendFile() is.
884      *
885      * @param textTransferOverrideSet the set of ICVSFiles to override, or null if none
886      */

887     public void setTextTransferOverride(Collection JavaDoc textTransferOverrideSet) {
888         this.textTransferOverrideSet = textTransferOverrideSet;
889     }
890     
891     /**
892      * Filter the provided global options using parameters set on this session
893      * or globally. The session may add global options that correspond to user
894      * preferences or remove those that contradict requirements for this
895      * particular session.
896      *
897      * @param globalOptions the global options, read-only
898      * @return the filtered global options
899      */

900     protected GlobalOption[] filterGlobalOptions(GlobalOption[] globalOptions) {
901         if (! Command.DO_NOT_CHANGE.isElementOf(globalOptions)) {
902             // Get the user preference for verbosity
903
QuietOption quietOption = CVSProviderPlugin.getPlugin().getQuietness();
904             if (quietOption != null) {
905                 globalOptions = quietOption.addToEnd(globalOptions);
906             }
907             // Get the user preference for read-only
908
if (isWatchEditEnabled()) {
909                 if (!Command.MAKE_READ_ONLY.isElementOf(globalOptions)) {
910                     globalOptions = Command.MAKE_READ_ONLY.addToEnd(globalOptions);
911                 }
912             }
913         }
914         return globalOptions;
915     }
916
917     private boolean isWatchEditEnabled() {
918         // First, look at the global preference
919
if (CVSProviderPlugin.getPlugin().getPluginPreferences().getBoolean(CVSProviderPlugin.READ_ONLY)) {
920             return true;
921         }
922         // If there is a provider, use the providers setting for watch/edit
923
try {
924             IResource resource = getLocalRoot().getIResource();
925             if (resource != null && resource.getType() != IResource.ROOT) {
926                 RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject(), CVSProviderPlugin.getTypeId());
927                 if (provider != null) {
928                     return ((CVSTeamProvider) provider).isWatchEditEnabled();
929                 }
930             }
931         } catch (CVSException e) {
932             CVSProviderPlugin.log(e);
933         }
934         return false;
935     }
936     
937     /**
938      * Method setIgnoringLocalChanges.
939      * @param b
940      */

941     protected void setIgnoringLocalChanges(boolean b) {
942         ignoringLocalChanges = b;
943     }
944     /**
945      * Returns the ignoringLocalChanges.
946      * @return boolean
947      */

948     protected boolean isIgnoringLocalChanges() {
949         return ignoringLocalChanges;
950     }
951
952     /*
953      * Get the response handler map to be used for this session. The map is created by making a copy of the global
954      * reponse handler map.
955      */

956     protected Map JavaDoc getReponseHandlers() {
957         if (responseHandlers == null) {
958             responseHandlers = Request.getReponseHandlerMap();
959         }
960         return responseHandlers;
961     }
962     
963     /*
964      * Makes a list of all valid responses; for initializing a session.
965      * @return a space-delimited list of all valid response strings
966      */

967     private String JavaDoc makeResponseList() {
968         StringBuffer JavaDoc result = new StringBuffer JavaDoc("ok error M E"); //$NON-NLS-1$
969
Iterator JavaDoc elements = getReponseHandlers().keySet().iterator();
970         while (elements.hasNext()) {
971             result.append(' ');
972             result.append((String JavaDoc) elements.next());
973         }
974         
975         return result.toString();
976     }
977     public void registerResponseHandler(ResponseHandler handler) {
978         getReponseHandlers().put(handler.getResponseID(), handler);
979     }
980     
981     public void removeResponseHandler(String JavaDoc responseID) {
982         getReponseHandlers().remove(responseID);
983     }
984     
985     public ResponseHandler getResponseHandler(String JavaDoc responseID) {
986         return (ResponseHandler)getReponseHandlers().get(responseID);
987     }
988
989     /**
990      * Accumulate the added errors so they can be included in the status returned
991      * when the command execution is finished. OK status are ignored.
992      * @param status the status to be accumulated
993      */

994     public void addError(IStatus status) {
995         if (!status.isOK())
996             errors.add(status);
997     }
998     
999     public boolean hasErrors() {
1000        return !errors.isEmpty();
1001    }
1002    
1003    public IStatus[] getErrors() {
1004        return (IStatus[]) errors.toArray(new IStatus[errors.size()]);
1005    }
1006    
1007    public void clearErrors() {
1008        errors.clear();
1009    }
1010
1011    public void setCurrentCommand(Command c) {
1012        currentCommand = c;
1013    }
1014    
1015    public Command getCurrentCommand() {
1016        return currentCommand;
1017    }
1018
1019    /**
1020     * Report the given error line to any listeners
1021     * @param line the error line
1022     * @param status the status that indicates any problems encountered parsing the line
1023     */

1024    public void handleErrorLine(String JavaDoc line, IStatus status) {
1025        ConsoleListeners.getInstance().errorLineReceived(this, line, status);
1026    }
1027    
1028    /**
1029     * An error has occurred while processing responses from the
1030     * server. Place this error is the status that will be returned
1031     * from the command and show the error in the console
1032     * @param status the status that descibes the error
1033     */

1034    public void handleResponseError(IStatus status) {
1035        addError(status);
1036        handleErrorLine(NLS.bind(CVSMessages.Session_0, new String JavaDoc[] { status.getMessage() }), status);
1037    }
1038}
1039
Popular Tags