KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > cvsclient > command > BasicCommand


1 /*****************************************************************************
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  *
7  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
8  * or http://www.netbeans.org/cddl.txt.
9  *
10  * When distributing Covered Code, include this CDDL Header Notice in each file
11  * and include the License file at http://www.netbeans.org/cddl.txt.
12  * If applicable, add the following below the CDDL Header, with the fields
13  * enclosed by brackets [] replaced by your own identifying information:
14  * "Portions Copyrighted [year] [name of copyright owner]"
15  *
16  * The Original Software is the CVS Client Library.
17  * The Initial Developer of the Original Software is Robert Greig.
18  * Portions created by Robert Greig are Copyright (C) 2000.
19  * All Rights Reserved.
20  *
21  * Contributor(s): Robert Greig.
22  *****************************************************************************/

23 package org.netbeans.lib.cvsclient.command;
24
25 import java.io.*;
26 import java.util.*;
27
28 import org.netbeans.lib.cvsclient.*;
29 import org.netbeans.lib.cvsclient.admin.*;
30 import org.netbeans.lib.cvsclient.connection.*;
31 import org.netbeans.lib.cvsclient.event.*;
32 import org.netbeans.lib.cvsclient.request.*;
33
34 /**
35  * A class that provides common functionality for many of the CVS command
36  * that send similar sequences of requests.
37  * @author Robert Greig
38  */

39 public abstract class BasicCommand extends BuildableCommand {
40     /**
41      * The requests that are sent and processed.
42      */

43     protected List requests = new LinkedList();
44
45     /**
46      * The client services that are provided to this command.
47      */

48     protected ClientServices clientServices;
49
50     /**
51      * Whether to update recursively.
52      */

53     private boolean recursive = true;
54
55     /**
56      * The files and/or directories to operate on.
57      */

58     protected File[] files;
59
60     /**
61      * Gets the value of the recursive option.
62      * @return true if recursive, false if not
63      * @deprecated use isRecursive instead
64      */

65     public boolean getRecursive() {
66         return recursive;
67     }
68
69     /**
70      * Gets the value of the recursive option.
71      * @return true if recursive, false if not
72      */

73     public boolean isRecursive() {
74         return recursive;
75     }
76
77     /**
78      * Sets the value of the recursive option.
79      * @param recursive true if the command should recurse, false otherwise
80      */

81     public void setRecursive(boolean recursive) {
82         this.recursive = recursive;
83     }
84
85     /**
86      * Set the files and/or directories on which to execute the command.
87      * The way these are processed is:<P>
88      * <UL><LI>Default action (i.e. not setting the files explicitly or
89      * setting them to <pre>null</pre>) is to use the directory in which
90      * the command was executed (see how directories are treated, below)</LI>
91      * <LI>Files are handled how you would expect</LI>
92      * <LI>For directories, all files within the directory are sent</LI></UL>
93      * @param theFiles the files to operate on. May be null to indicate that the
94      * local directory specified in the client should be used. Full, absolute
95      * canonical pathnames <b>must</b> be supplied.
96      */

97     public void setFiles(File[] theFiles) {
98         // sort array.. files first, directories follow
99
if (theFiles == null) {
100             files = theFiles;
101             return;
102         }
103
104 // assert theFiles.length > 0 : "Empty array causes random AIOOBEs!"; // ClientRuntime.java:119
105

106         files = new File[theFiles.length];
107         int fileCount = 0;
108         int dirCount = 0;
109         int totalCount = theFiles.length;
110         for (int index = 0; index < totalCount; index++) {
111             File currentFile = theFiles[index];
112             if (currentFile.isDirectory()) {
113                 files[totalCount - (1 + dirCount)] = currentFile;
114                 dirCount = dirCount + 1;
115             }
116             else {
117                 files[fileCount] = currentFile;
118                 fileCount = fileCount + 1;
119             }
120         }
121     }
122
123     /**
124      * Get the files and/or directories specified for this command to operate
125      * on.
126      * @return the array of Files
127      */

128     public File[] getFiles() {
129         return files;
130     }
131
132     /**
133      * Get a single file from the "files" list. returns only files, not directories.
134      * This method is used from within the builders, because for single file requests, the
135      * cvs server doesn't return us enough information to identify what file has been returned.
136      * Thus we sort the "files" array (files come before directories. Then the response froms erver comes in the same order
137      * and the files can be found this way.
138      *
139      * @param index the index of the file in the list.
140      */

141     public File getXthFile(int index) {
142         if (index < 0 || index >= files.length) {
143             return null;
144         }
145         File file = files[index];
146         if (!file.isFile()) {
147             return null;
148         }
149         return file;
150     }
151
152     /**
153      * @param ending - the ending part of the file's pathname.. path separator is cvs's default '/'
154      */

155     public File getFileEndingWith(String JavaDoc ending) {
156         String JavaDoc locEnding = ending.replace('\\', '/');
157         String JavaDoc localDir = getLocalDirectory().replace('\\','/');
158         int index = 0;
159         for (index = 0; index < files.length; index++) {
160             String JavaDoc path = files[index].getAbsolutePath();
161             String JavaDoc parentPath = files[index].getParentFile().getAbsolutePath().replace('\\', '/');
162             path = path.replace('\\', '/');
163             if ((path.endsWith(locEnding) && locEnding.indexOf('/') >= 0) ||
164                    (files[index].getName().equals(locEnding) && parentPath.equals(localDir))) {
165                 return files[index];
166             }
167         }
168         return null;
169     }
170
171
172     /**
173      * Add the appropriate requests for a specified path. For a directory,
174      * process all the files within that directory and for a single file,
175      * just send it. For each directory, send a directory request. For each
176      * file send an Entry request followed by a Modified request.
177      * @param path the particular path to issue requests for. May be
178      * either a file or a directory.
179      */

180     private void addRequests(File path)
181             throws FileNotFoundException, IOException, CommandAbortedException {
182         if (path == null) {
183             throw new IllegalArgumentException JavaDoc("Cannot add requests for a " +
184                                                "null path.");
185         }
186
187         if (!path.exists() || path.isFile()) {
188             addRequestsForFile(path);
189         }
190         else {
191             addRequestsForDirectory(path);
192         }
193     }
194
195     /**
196      * Should return true if unchanged files should not be sent to server.
197      * If false is returned, all files will be sent to server
198      * This method is used by <code>sendEntryAndModifiedRequests</code>.
199      */

200     protected boolean doesCheckFileTime() {
201         return true;
202     }
203
204     /**
205      * Send an Entry followed by a Modified or Unchanged request based on
206      * whether the file has been untouched on the local machine.
207      *
208      * @param entry the entry for the file
209      * @param file the file in question
210      */

211     protected void sendEntryAndModifiedRequests(Entry entry,
212                                                 File file) {
213         if (entry == null) {
214             return;
215         }
216
217         // for deleted added files, don't send anything..
218
if (file != null && !file.exists() && entry.isNewUserFile()) {
219             return;
220         }
221
222         Date entryLastModified = entry.getLastModified();
223         boolean hadConflicts = entry.hadConflicts();
224         if (!hadConflicts) {
225             // we null out the conflict field if there is no conflict
226
// because we use that field to store the timestamp of the
227
// file (just like command-line CVS). There is no point
228
// in sending this information to the CVS server, even
229
// though it should be ignored by the server.
230
entry.setConflict(null);
231         } else if (fileRequiresConflictResolution(file, entry)) {
232             // send entry in wire conflict format
233
Entry clone = new Entry(entry.toString());
234             clone.setConflict(Entry.HAD_CONFLICTS_AND_TIMESTAMP_MATCHES_FILE);
235             entry = clone;
236         }
237         addRequest(new EntryRequest(entry));
238
239         if (file == null || !file.exists() || entry.isUserFileToBeRemoved()) {
240             return;
241         }
242
243         if (doesCheckFileTime() && !hadConflicts && entryLastModified != null) {
244             if (DateComparator.getInstance().equals(file.lastModified(),
245                                                     entryLastModified.getTime())) {
246                 addRequest(new UnchangedRequest(file.getName()));
247                 return;
248             }
249         }
250
251         addRequest(new ModifiedRequest(file, entry.isBinary()));
252     }
253
254     /**
255      * When committing, we need to perform a check that will prevent the
256      * unmodified conflicting files from being checked in. This is the behavior
257      * of command line CVS client. This method checks the Entry for the file
258      * against the time stamp. The user can optimally call this method only if
259      * the Entry for the file indicates a conflict
260      * @param entry The Entry object corresponding to the file
261      * @param file The File object representing the file on the filesystem
262      * @return boolean Returns true if the file's timestamp is same or less than
263      * the time when the conflicting merge was done by CVS update as indicated
264      * by the Entry.
265      */

266     private final boolean fileRequiresConflictResolution(File file, Entry entry) {
267
268         if (file == null) return false; // null file is set by clean copy
269

270         boolean ret = false;
271
272         if (entry.hadConflicts()) {
273             // TODO introduce common timestamp comparation logic
274
// We only test accuracy of upto a second (1000ms) because that is
275
// the precision of the timestamp in the entries file
276
long mergedTime = entry.getLastModified().getTime() / 1000;
277             long timeStampOfFile = file.lastModified() / 1000;
278             ret = timeStampOfFile <= mergedTime;
279         }
280
281         return ret;
282     }
283
284
285     /**
286      * Adds the appropriate requests for a given directory. Sends a
287      * directory request followed by as many Entry and Modified requests
288      * as required
289      * @param directory the directory to send requests for
290      * @throws IOException if an error occurs constructing the requests
291      */

292     protected void addRequestsForDirectory(File directory)
293             throws IOException, CommandAbortedException {
294         if (!clientServices.exists(directory)) {
295             return;
296         }
297         if (clientServices.isAborted()) {
298             throw new CommandAbortedException("Command aborted during request generation", "Command aborted during request generation");
299         }
300
301         addDirectoryRequest(directory);
302
303         File [] dirFiles = directory.listFiles();
304         List localFiles;
305         if (dirFiles == null) {
306             localFiles = new ArrayList(0);
307         } else {
308             localFiles = new ArrayList(Arrays.asList(dirFiles));
309             localFiles.remove(new File(directory, "CVS"));
310         }
311
312         List subDirectories = null;
313         if (isRecursive()) {
314             subDirectories = new LinkedList();
315         }
316
317         // get all the entries we know about, and process them
318
for (Iterator it = clientServices.getEntries(directory); it.hasNext();) {
319             final Entry entry = (Entry)it.next();
320             final File file = new File(directory, entry.getName());
321             if (entry.isDirectory()) {
322                 if (isRecursive()) {
323                     subDirectories.add(new File(directory, entry.getName()));
324                 }
325             }
326             else {
327                 addRequestForFile(file, entry);
328             }
329             localFiles.remove(file);
330         }
331         
332         // In case that CVS folder does not exist, we need to process all
333
// directories that have CVS subfolders:
334
if (isRecursive() && !new File(directory, "CVS").exists()) {
335             File[] subFiles = directory.listFiles();
336             if (subFiles != null) {
337                 for (int i = 0; i < subFiles.length; i++) {
338                     if (subFiles[i].isDirectory() && new File(subFiles[i], "CVS").exists()) {
339                         subDirectories.add(subFiles[i]);
340                     }
341                 }
342             }
343         }
344
345         for (Iterator it = localFiles.iterator(); it.hasNext();) {
346             String JavaDoc localFileName = ((File)it.next()).getName();
347             if (!clientServices.shouldBeIgnored(directory, localFileName)) {
348                 addRequest(new QuestionableRequest(localFileName));
349             }
350         }
351
352         if (isRecursive()) {
353             for (Iterator it = subDirectories.iterator(); it.hasNext();) {
354                 File subdirectory = (File)it.next();
355                 File cvsSubDir = new File(subdirectory, "CVS"); //NOI18N
356
if (clientServices.exists(cvsSubDir)) {
357                     addRequestsForDirectory(subdirectory);
358                 }
359             }
360         }
361     }
362
363     /**
364      * This method is called for each explicit file and for files within a
365      * directory.
366      */

367     protected void addRequestForFile(File file, Entry entry) {
368         sendEntryAndModifiedRequests(entry, file);
369     }
370
371     /**
372      * Add the appropriate requests for a single file. A directory request
373      * is sent, followed by an Entry and Modified request
374      * @param file the file to send requests for
375      * @throws IOException if an error occurs constructing the requests
376      */

377     protected void addRequestsForFile(File file) throws IOException {
378         addDirectoryRequest(file.getParentFile());
379
380         try {
381             final Entry entry = clientServices.getEntry(file);
382             // a non-null entry means the file does exist in the
383
// Entries file for this directory
384
if (entry != null) {
385                 addRequestForFile(file, entry);
386             } else if (file.exists()) {
387                 // #50963 file exists locally without an entry AND the request is
388
// for the file explicitly
389
boolean unusedBinaryFlag = false;
390                 addRequest(new ModifiedRequest(file, unusedBinaryFlag));
391             }
392         }
393         catch (IOException ex) {
394             System.err.println("An error occurred getting the Entry " +
395                                "for file " + file + ": " + ex);
396             ex.printStackTrace();
397         }
398     }
399
400     /**
401      * Adds a DirectoryRequest (and maybe a StickyRequest) to the request list.
402      */

403     protected final void addDirectoryRequest(File directory) {
404         // remove localPath prefix from directory. If left with
405
// nothing, use dot (".") in the directory request
406
String JavaDoc dir = getRelativeToLocalPathInUnixStyle(directory);
407
408         try {
409             String JavaDoc repository = clientServices.getRepositoryForDirectory(
410                     directory.getAbsolutePath());
411             addRequest(new DirectoryRequest(dir, repository));
412             String JavaDoc tag = clientServices.getStickyTagForDirectory(directory);
413             if (tag != null) {
414                 addRequest(new StickyRequest(tag));
415             }
416         }
417         catch (FileNotFoundException ex) {
418             // we can ignore this exception safely because it just means
419
// that the user has deleted a directory referenced in a
420
// CVS/Entries file
421
}
422         catch (IOException ex) {
423             System.err.println("An error occurred reading the respository " +
424                                "for the directory " + dir + ": " + ex);
425             ex.printStackTrace();
426         }
427     }
428
429     /**
430      * Add the argument requests. The argument requests are created using
431      * the original set of files/directories passed in. Subclasses of this
432      * class should call this method at the appropriate point in their
433      * execute() method. Note that arguments are appended to the list.
434      */

435     protected void addArgumentRequests() {
436         if (files == null) {
437             return;
438         }
439
440         for (int i = 0; i < files.length; i++) {
441             final File file = files[i];
442             String JavaDoc relativePath = getRelativeToLocalPathInUnixStyle(file);
443             addRequest(new ArgumentRequest(relativePath));
444         }
445     }
446
447     /**
448      * Execute a command. This implementation sends a Root request, followed
449      * by as many Directory and Entry requests as is required by the recurse
450      * setting and the file arguments that have been set. Subclasses should
451      * call this first, and tag on the end of the requests list any further
452      * requests and, finally, the actually request that does the command (e.g.
453      * <pre>update</pre>, <pre>status</pre> etc.)
454      * @param client the client services object that provides any necessary
455      * services to this command, including the ability to actually process
456      * all the requests
457      * @throws CommandException if an error occurs executing the command
458      */

459     public void execute(ClientServices client, EventManager em)
460             throws CommandException, AuthenticationException {
461         requests.clear();
462         clientServices = client;
463         super.execute(client, em);
464
465         if (client.isFirstCommand()) {
466             addRequest(new RootRequest(client.getRepository()));
467         }
468
469         addFileRequests();
470     }
471
472     private void addFileRequests() throws CommandException {
473         try {
474             if (files != null && files.length > 0) {
475                 for (int i = 0; i < files.length; i++) {
476                     addRequests(files[i]);
477                 }
478             }
479             else {
480                 // if no arguments have been specified, then specify the
481
// local directory - the "top level" for this command
482
if (assumeLocalPathWhenUnspecified()) {
483                     addRequests(new File(getLocalDirectory()));
484                 }
485             }
486         }
487         catch (Exception JavaDoc ex) {
488             throw new CommandException(ex, ex.getLocalizedMessage());
489         }
490     }
491
492     /**
493      * The result from this command is used only when the getFiles() returns null or empty array.
494      * in such a case and when this method returns true, it is assumed the localpath should be taken
495      * as the 'default' file for the building of requests.
496      * Generally assumed to be true. Can be overriden by subclasses. However make sure you know what you are doing. :)
497      */

498     protected boolean assumeLocalPathWhenUnspecified() {
499         return true;
500     }
501
502     /**
503      * Adds the specified request to the request list.
504      */

505     protected final void addRequest(Request request) {
506         requests.add(request);
507     }
508
509     /**
510      * Adds the request for the current working directory.
511      */

512     protected final void addRequestForWorkingDirectory(ClientServices clientServices)
513             throws IOException {
514         addRequest(new DirectoryRequest(".", //NOI18N
515
clientServices.getRepositoryForDirectory(getLocalDirectory())));
516     }
517
518     /**
519      * If the specified value is true, add a ArgumentRequest for the specified
520      * argument.
521      */

522     protected final void addArgumentRequest(boolean value, String JavaDoc argument) {
523         if (!value) {
524             return;
525         }
526
527         addRequest(new ArgumentRequest(argument));
528     }
529
530     /**
531      * Appends the file's names to the specified buffer.
532      */

533     protected final void appendFileArguments(StringBuffer JavaDoc buffer) {
534         File[] files = getFiles();
535         if (files == null) {
536             return;
537         }
538
539         for (int index = 0; index < files.length; index++) {
540             if (index > 0) {
541                 buffer.append(' ');
542             }
543             buffer.append(files[index].getName());
544         }
545     }
546 }
547
548
Popular Tags