KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > cvsclient > command > update > UpdateCommand


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  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is the CVS Client Library.
16  * The Initial Developer of the Original Software is Robert Greig.
17  * Portions created by Robert Greig are Copyright (C) 2000.
18  * All Rights Reserved.
19  *
20  * Contributor(s): Robert Greig.
21  *****************************************************************************/

22 package org.netbeans.lib.cvsclient.command.update;
23
24 import java.io.*;
25 import java.text.*;
26 import java.util.*;
27
28 import org.netbeans.lib.cvsclient.*;
29 import org.netbeans.lib.cvsclient.file.FileUtils;
30 import org.netbeans.lib.cvsclient.admin.*;
31 import org.netbeans.lib.cvsclient.command.*;
32 import org.netbeans.lib.cvsclient.connection.*;
33 import org.netbeans.lib.cvsclient.event.*;
34 import org.netbeans.lib.cvsclient.request.*;
35
36 /**
37  * The Update command. Updates files that have previously been checked out
38  * from the repository with the checkout command. Modified files are not
39  * overwritten.
40  * @author Robert Greig
41  */

42 public class UpdateCommand extends BasicCommand
43         implements TemporaryFileCreator {
44
45     // This format possibly may be set by the user of the library later.
46
private static final String JavaDoc RENAME_FORMAT = "{0}/.#{1}.{2}"; //NOI18N
47
private static final Object JavaDoc[] FORMAT_PARAMETER = new Object JavaDoc[3]; // { path, filename, revision }
48

49     /**
50      * A store of potentially empty directories. When a directory has a file
51      * in it, it is removed from this set. This set allows the prune option
52      * to be implemented.
53      */

54     private final Set emptyDirectories = new HashSet();
55     /**
56      * Whether to build directories, like checkout does (this is the -d option
57      * in command-line CVS).
58      */

59     private boolean buildDirectories;
60
61     /**
62      * Determines whether to get a clean copy from the server.
63      * This overrides even locally modified files.
64      */

65     private boolean cleanCopy;
66
67     /**
68      * Whether to prune directories, i.e. remove any directories that do not
69      * contain any files. This is the -P option in command-line CVS).
70      */

71     private boolean pruneDirectories;
72
73     /**
74      * Determines wheather the output of the command is processed on standard output.
75      * Default is false. If true, nothing is done to local working files.
76      */

77     private boolean pipeToOutput;
78
79     /**
80      * Resets any sticky tags/dates/options imposed on the updated file(s).
81      */

82     private boolean resetStickyOnes;
83
84     /**
85      * Use head revision if a revision meeting criteria set by switches -r/-D
86      * (tag/date) is not found.
87      */

88     private boolean useHeadIfNotFound;
89
90     /**
91      * equals the -D switch of command line cvs.
92      */

93     private String JavaDoc updateByDate;
94
95     /**
96      * Equals the -r switch of command-line cvs.
97      */

98     private String JavaDoc updateByRevision;
99
100     /**
101      * Use this keyword substitution for the command.
102      * does not include the -k switch part.
103      */

104     private KeywordSubstitutionOptions keywordSubst;
105
106     /**
107      * First of the 2 possible -j switches that merge 2 different revisions.
108      * If only this property is set, the current working file is merged
109      * with the specified one.
110      */

111     private String JavaDoc mergeRevision1;
112
113     /**
114      * Second of the 2 possible -j switches that merge 2 different revisions.
115      * Assumes the first -j switch (mergeRevision1 property) is set.
116      * Then the update commands merges the sources of these 2 revisons specified
117      * by the -j switches.
118      */

119     private String JavaDoc mergeRevision2;
120
121     /**
122      * Construct a new update command.
123      */

124     public UpdateCommand() {
125         // TODO: move up the hierarchy ?
126
resetCVSCommand();
127     }
128
129     /**
130      * Method that is called while the command is being executed.
131      * Descendants can override this method to return a Builder instance
132      * that will parse the server's output and create data structures.
133      */

134     public Builder createBuilder(EventManager eventManager) {
135         if (isPipeToOutput()) {
136             return new PipedFilesBuilder(eventManager, this, this);
137         }
138         return new UpdateBuilder(eventManager, getLocalDirectory());
139     }
140
141     /**
142      * If <code>getCleanCopy()</code> returns true, the files will be treated
143      * as not existing.
144      */

145     protected void sendEntryAndModifiedRequests(Entry entry, File file) {
146         if (isCleanCopy() && file != null && entry != null) {
147             if (!isPipeToOutput()) {
148                 FORMAT_PARAMETER[0] = file.getParent();
149                 FORMAT_PARAMETER[1] = file.getName();
150                 FORMAT_PARAMETER[2] = entry.getRevision();
151
152                 String JavaDoc filename = MessageFormat.format(RENAME_FORMAT, FORMAT_PARAMETER);
153                 try {
154                     FileUtils.copyFile(file, new File(filename));
155                 } catch (IOException e) {
156                     // backup copy will not be created
157
}
158             }
159             file = null;
160         }
161         super.sendEntryAndModifiedRequests(entry, file);
162     }
163
164     /**
165      * Set whether to build directories. This is the -d option in command-line
166      * CVS.
167      */

168     public void setBuildDirectories(boolean buildDirectories) {
169         this.buildDirectories = buildDirectories;
170     }
171
172     /**
173      * Returns whether to build directories.
174      * @return true if directories are to be built, false otherwise
175      */

176     public boolean isBuildDirectories() {
177         return buildDirectories;
178     }
179
180     /**
181      * Sets whether to get a clean copy from the server.
182      * Even locally modified files will not merged but overridden.
183      * This is the -C option in the command-line CVS.
184      */

185     public void setCleanCopy(boolean cleanCopy) {
186         this.cleanCopy = cleanCopy;
187     }
188
189     /**
190      * Returns whether to get a clean copy from the server.
191      */

192     public boolean isCleanCopy() {
193         return cleanCopy;
194     }
195
196     /**
197      * Set whether to prune directories. This is the -P option in the command-
198      * line CVS.
199      */

200     public void setPruneDirectories(boolean pruneDirectories) {
201         this.pruneDirectories = pruneDirectories;
202     }
203
204     /**
205      * Returns whether to prune directories.
206      * @return true if directories should be removed if they contain no files,
207      * false otherwise.
208      */

209     public boolean isPruneDirectories() {
210         return pruneDirectories;
211     }
212
213     /**
214      * Execute the command.
215      * @param client the client services object that provides any necessary
216      * services to this command, including the ability to actually
217      * process all the requests
218      */

219     public void execute(ClientServices client, EventManager eventManager)
220             throws CommandException, AuthenticationException {
221         client.ensureConnection();
222
223         super.execute(client, eventManager);
224
225         emptyDirectories.clear();
226
227         try {
228             // now add the request that indicates the working directory for the
229
// command
230
if (!isRecursive()) {
231                 requests.add(1, new ArgumentRequest("-l")); //NOI18N
232
}
233             if (isBuildDirectories()) {
234                 requests.add(1, new ArgumentRequest("-d")); //NOI18N
235
}
236             if (isCleanCopy() && !isPipeToOutput()) {
237                 requests.add(1, new ArgumentRequest("-C")); //NOI18N
238
}
239             if (isPipeToOutput()) {
240                 requests.add(1, new ArgumentRequest("-p")); //NOI18N
241
}
242             if (isResetStickyOnes()) {
243                 requests.add(1, new ArgumentRequest("-A")); //NOI18N
244
}
245             if (isUseHeadIfNotFound()) {
246                 requests.add(1, new ArgumentRequest("-f")); //NOI18N
247
}
248             if (getUpdateByDate() != null) {
249                 requests.add(1, new ArgumentRequest("-D")); //NOI18N
250
requests.add(2, new ArgumentRequest(getUpdateByDate()));
251             }
252             else if (getUpdateByRevision() != null) {
253                 requests.add(1, new ArgumentRequest("-r")); //NOI18N
254
requests.add(2, new ArgumentRequest(getUpdateByRevision()));
255             }
256             if (getMergeRevision1() != null) {
257                 requests.add(1, new ArgumentRequest("-j")); //NOI18N
258
requests.add(2, new ArgumentRequest(getMergeRevision1()));
259
260                 if (getMergeRevision2() != null) {
261                     requests.add(3, new ArgumentRequest("-j")); //NOI18N
262
requests.add(4, new ArgumentRequest(getMergeRevision2()));
263                 }
264             }
265             if (getKeywordSubst() != null) {
266                 requests.add(1, new ArgumentRequest("-k")); //NOI18N
267
requests.add(2, new ArgumentRequest(getKeywordSubst().toString()));
268             }
269             requests.add(1, new ArgumentRequest("-u")); //NOI18N
270

271             addRequestForWorkingDirectory(client);
272             addArgumentRequests();
273             addRequest(CommandRequest.UPDATE);
274
275             // hack - now check for the entry request for removed files
276
// only when p with -r or -D is on
277
if (isPipeToOutput() && (getUpdateByRevision() != null || getUpdateByDate() != null)) {
278                 ListIterator it = requests.listIterator();
279                 while (it.hasNext()) {
280                     Object JavaDoc req = it.next();
281                     if (req instanceof EntryRequest) {
282                         EntryRequest eReq = (EntryRequest)req;
283                         Entry entry = eReq.getEntry();
284                         if (entry.getRevision().startsWith("-")) {//NOI18N
285
entry.setRevision(entry.getRevision().substring(1));
286                         }
287                         it.set(new EntryRequest(entry));
288                         it.add(new UnchangedRequest(entry.getName()));
289                     }
290                 }
291             }
292             // end of hack..
293
client.processRequests(requests);
294             if (pruneDirectories && (getGlobalOptions() == null || !getGlobalOptions().isDoNoChanges())) {
295                 pruneEmptyDirectories(client);
296             }
297         }
298         catch (CommandException ex) {
299             throw ex;
300         }
301         catch (EOFException ex) {
302             throw new CommandException(ex, CommandException.getLocalMessage("CommandException.EndOfFile", null)); //NOI18N
303
}
304         catch (Exception JavaDoc ex) {
305             throw new CommandException(ex, ex.getLocalizedMessage());
306         }
307         finally {
308             requests.clear();
309         }
310     }
311
312     /**
313      * Getter for property pipeToOutput.
314      * @return Value of property pipeToOutput.
315      */

316     public boolean isPipeToOutput() {
317         return pipeToOutput;
318     }
319
320     /**
321      * Setter for property pipeToOutput.
322      * @param pipeToOutput New value of property pipeToOutput.
323      */

324     public void setPipeToOutput(boolean pipeToOutput) {
325         this.pipeToOutput = pipeToOutput;
326     }
327
328     /**
329      * Getter for property resetStickyOnes.
330      * @return Value of property resetStickyOnes.
331      */

332     public boolean isResetStickyOnes() {
333         return resetStickyOnes;
334     }
335
336     /**
337      * Setter for property resetStickyOnes.
338      * @param resetStickyOnes New value of property resetStickyOnes.
339      */

340     public void setResetStickyOnes(boolean resetStickyOnes) {
341         this.resetStickyOnes = resetStickyOnes;
342     }
343
344     /**
345      * Getter for property useHeadIfNotFound.
346      * @return Value of property useHeadIfNotFound.
347      */

348     public boolean isUseHeadIfNotFound() {
349         return useHeadIfNotFound;
350     }
351
352     /**
353      * Setter for property useHeadIfNotFound.
354      * @param useHeadIfNotFound New value of property useHeadIfNotFound.
355      */

356     public void setUseHeadIfNotFound(boolean useHeadIfNotFound) {
357         this.useHeadIfNotFound = useHeadIfNotFound;
358     }
359
360     /**
361      * Getter for property updateByDate.
362      * @return Value of property updateByDate.
363      */

364     public String JavaDoc getUpdateByDate() {
365         return updateByDate;
366     }
367
368     /**
369      * Setter for property updateByDate.
370      * @param updateByDate New value of property updateByDate.
371      */

372     public void setUpdateByDate(String JavaDoc updateByDate) {
373         this.updateByDate = getTrimmedString(updateByDate);
374     }
375
376     /**
377      * Getter for property updateByRevision.
378      * @return Value of property updateByRevision.
379      */

380     public String JavaDoc getUpdateByRevision() {
381         return updateByRevision;
382     }
383
384     /**
385      * Setter for property updateByRevision.
386      * @param updateByRevision New value of property updateByRevision.
387      */

388     public void setUpdateByRevision(String JavaDoc updateByRevision) {
389         this.updateByRevision = getTrimmedString(updateByRevision);
390     }
391
392     /**
393      * Getter for property keywordSubst.
394      * @return Value of property keywordSubst.
395      */

396     public KeywordSubstitutionOptions getKeywordSubst() {
397         return keywordSubst;
398     }
399
400     /**
401      * Setter for property keywordSubst.
402      * @param keywordSubst New value of property keywordSubst.
403      */

404     public void setKeywordSubst(KeywordSubstitutionOptions keywordSubst) {
405         this.keywordSubst = keywordSubst;
406     }
407
408     /**
409      * Method that creates a temporary file.
410      */

411     public File createTempFile(String JavaDoc filename) throws IOException {
412         File temp = File.createTempFile("cvs", ".dff", getGlobalOptions().getTempDir()); //NOI18N
413
return temp;
414     }
415
416     /**
417      * This method returns how the command would looklike when typed on the
418      * command line.
419      * Each command is responsible for constructing this information.
420      * @returns <command's name> [<parameters>] files/dirs.
421      * Example: checkout -p CvsCommand.java
422      */

423     public String JavaDoc getCVSCommand() {
424         StringBuffer JavaDoc toReturn = new StringBuffer JavaDoc("update "); //NOI18N
425
toReturn.append(getCVSArguments());
426         File[] files = getFiles();
427         if (files != null) {
428             for (int index = 0; index < files.length; index++) {
429                 toReturn.append(files[index].getName());
430                 toReturn.append(' ');
431             }
432         }
433         return toReturn.toString();
434     }
435
436     /**
437      * Returns the arguments of the command in the command-line style.
438      * Similar to getCVSCommand() however without the files and command's name
439      */

440     public String JavaDoc getCVSArguments() {
441         StringBuffer JavaDoc toReturn = new StringBuffer JavaDoc(""); //NOI18N
442
if (isPipeToOutput()) {
443             toReturn.append("-p "); //NOI18N
444
}
445         if (isCleanCopy()) {
446             toReturn.append("-C "); //NOI18N
447
}
448         if (!isRecursive()) {
449             toReturn.append("-l "); //NOI18N
450
}
451         if (isBuildDirectories()) {
452             toReturn.append("-d "); //NOI18N
453
}
454         if (isPruneDirectories()) {
455             toReturn.append("-P "); //NOI18N
456
}
457         if (isResetStickyOnes()) {
458             toReturn.append("-A "); //NOI18N
459
}
460         if (isUseHeadIfNotFound()) {
461             toReturn.append("-f "); //NOI18N
462
}
463         if (getKeywordSubst() != null) {
464             toReturn.append("-k"); //NOI18N
465
toReturn.append(getKeywordSubst().toString());
466             toReturn.append(' ');
467         }
468         if (getUpdateByRevision() != null) {
469             toReturn.append("-r "); //NOI18N
470
toReturn.append(getUpdateByRevision());
471             toReturn.append(' ');
472         }
473         if (getUpdateByDate() != null) {
474             toReturn.append("-D "); //NOI18N
475
toReturn.append(getUpdateByDate());
476             toReturn.append(' ');
477         }
478         if (getMergeRevision1() != null) {
479             toReturn.append("-j "); //NOI18N
480
toReturn.append(getMergeRevision1());
481             toReturn.append(' ');
482
483             if (getMergeRevision2() != null) {
484                 toReturn.append("-j "); //NOI18N
485
toReturn.append(getMergeRevision2());
486                 toReturn.append(' ');
487             }
488         }
489         return toReturn.toString();
490     }
491
492     /**
493      * Takes the arguments and by parsing them, sets the command. To be mainly
494      * used for automatic settings (like parsing the .cvsrc file)
495      */

496     public boolean setCVSCommand(char opt, String JavaDoc optArg) {
497         if (opt == 'R') {
498             setRecursive(true);
499         }
500         else if (opt == 'C') {
501             setCleanCopy(true);
502         }
503         else if (opt == 'l') {
504             setRecursive(false);
505         }
506         else if (opt == 'd') {
507             setBuildDirectories(true);
508         }
509         else if (opt == 'P') {
510             setPruneDirectories(true);
511         }
512         else if (opt == 'A') {
513             setResetStickyOnes(true);
514         }
515         else if (opt == 'f') {
516             setUseHeadIfNotFound(true);
517         }
518         else if (opt == 'D') {
519             setUpdateByDate(optArg.trim());
520         }
521         else if (opt == 'r') {
522             setUpdateByRevision(optArg.trim());
523         }
524         else if (opt == 'k') {
525             KeywordSubstitutionOptions keywordSubst =
526                     KeywordSubstitutionOptions.findKeywordSubstOption(optArg);
527             setKeywordSubst(keywordSubst);
528         }
529         else if (opt == 'p') {
530             setPipeToOutput(true);
531         }
532         else if (opt == 'j') {
533             if (getMergeRevision1() == null) {
534                 setMergeRevision1(optArg);
535             }
536             else {
537                 setMergeRevision2(optArg);
538             }
539         }
540         else {
541             // TODO - now silently ignores not recognized switches.
542
return false;
543         }
544         return true;
545     }
546
547     /**
548      * Resets all switches in the command.
549      * After calling this method, the command should have no switches defined
550      * and should behave defaultly.
551      */

552     public void resetCVSCommand() {
553         setRecursive(true);
554         setCleanCopy(false);
555         setBuildDirectories(false);
556         setPruneDirectories(false);
557         setResetStickyOnes(false);
558         setUseHeadIfNotFound(false);
559         setUpdateByDate(null);
560         setUpdateByRevision(null);
561         setKeywordSubst(null);
562         setPipeToOutput(false);
563         setMergeRevision1(null);
564         setMergeRevision2(null);
565     }
566
567     /**
568      * Called when the server wants to send a message to be displayed to
569      * the user. The message is only for information purposes and clients
570      * can choose to ignore these messages if they wish.
571      * @param e the event
572      */

573     public void messageSent(MessageEvent e) {
574         super.messageSent(e);
575         // we use this event to determine which directories need to be checked
576
// for updating
577
if (!pruneDirectories) {
578             return;
579         }
580
581         final String JavaDoc relativePath = CommandUtils.getExaminedDirectory(e.getMessage(),
582                                                                       UpdateBuilder.EXAM_DIR);
583         if (relativePath == null) {
584             return;
585         }
586
587         // dont delete the current directory, even if it's empty
588
if (relativePath.equals(".")) { //NOI18N
589
return;
590         }
591
592         emptyDirectories.add(new File(getLocalDirectory(), relativePath));
593     }
594
595     /**
596      * Prunes a directory, recursively pruning its subdirectories
597      * @param directory the directory to prune
598      */

599     private boolean pruneEmptyDirectory(File directory, ClientServices client)
600             throws IOException {
601         final File[] contents = directory.listFiles();
602
603         // should never be null, but just in case...
604
if (contents == null) {
605             return true;
606         }
607
608         for (int i = 0; i < contents.length; i++) {
609             if (contents[i].isFile()) {
610                 return false;
611             }
612
613             // Skip the cvs directory
614
if (contents[i].getName().equals("CVS")) {
615                 //NOI18N
616
continue;
617             }
618
619             if (!pruneEmptyDirectory(contents[i], client)) {
620                 return false;
621             }
622         }
623
624         // check this is a CVS directory and not some directory the user
625
// has stupidly called CVS...
626
if (new File(directory, "CVS/Entries").isFile() && new File(directory, "CVS/Repository").isFile()) {
627             final File adminDir = new File(directory, "CVS"); //NOI18N
628
// we must NOT delete a directory if it contains valuable entries
629
for (Iterator i = clientServices.getEntries(directory); i.hasNext(); ) {
630                 Entry entry = (Entry) i.next();
631                 if (entry.getName() != null && entry.isUserFileToBeRemoved()) return false;
632             }
633             deleteRecursively(adminDir);
634             directory.delete();
635             // if the client still makes this directory's entries available, do not delete its entry
636
if (!client.exists(directory)) client.removeEntry(directory);
637             return true;
638         }
639
640         return false;
641     }
642
643     /**
644      * Deletes a directory and all files and directories inside the directory.
645      *
646      * @param dir directory to delete
647      */

648     private void deleteRecursively(File dir) {
649       File [] files = dir.listFiles();
650       for (int i = 0; i < files.length; i++) {
651         File file = files[i];
652         if (file.isDirectory()) {
653           deleteRecursively(file);
654         } else {
655           file.delete();
656         }
657       }
658       dir.delete();
659     }
660
661     /**
662      * Remove any directories that don't contain any files
663      */

664     private void pruneEmptyDirectories(ClientServices client)
665             throws IOException {
666         for (Iterator it = emptyDirectories.iterator(); it.hasNext();) {
667             final File dir = (File)it.next();
668             // we might have deleted it already (due to recursive delete)
669
// so we need to check existence
670
if (dir.exists()) {
671                 pruneEmptyDirectory(dir, client);
672             }
673         }
674         emptyDirectories.clear();
675     }
676
677     /**
678      * String returned by this method defines which options are available for this particular command
679      */

680     public String JavaDoc getOptString() {
681         return "RCnldPAfD:r:pj:k:"; //NOI18N
682
}
683
684     /** Getter for property mergeRevision1.
685      * @return Value of property mergeRevision1.
686      */

687     public String JavaDoc getMergeRevision1() {
688         return mergeRevision1;
689     }
690
691     /** Setter for property mergeRevision1.
692      * @param mergeRevision1 New value of property mergeRevision1.
693      */

694     public void setMergeRevision1(String JavaDoc mergeRevision1) {
695         this.mergeRevision1 = getTrimmedString(mergeRevision1);
696     }
697
698     /** Getter for property mergeRevision2.
699      * @return Value of property mergeRevision2.
700      */

701     public String JavaDoc getMergeRevision2() {
702         return mergeRevision2;
703     }
704
705     /** Setter for property mergeRevision2.
706      * @param mergeRevision2 New value of property mergeRevision2.
707      */

708     public void setMergeRevision2(String JavaDoc mergeRevision2) {
709         this.mergeRevision2 = getTrimmedString(mergeRevision2);
710     }
711 }
712
Popular Tags