KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > cvsclient > admin > StandardAdminHandler


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.admin;
23
24 import java.io.*;
25 import java.util.*;
26
27 import org.netbeans.lib.cvsclient.command.*;
28 import org.netbeans.lib.cvsclient.file.FileUtils;
29
30 /**
31  * A handler for administrative information that maintains full compatibility
32  * with the one employed by the original C implementation of a CVS client.
33  * <p>This implementation strives to provide complete compatibility with
34  * the standard CVS client, so that operations on locally checked-out
35  * files can be carried out by either this library or the standard client
36  * without causing the other to fail. Any such failure should be considered
37  * a bug in this library.
38  * @author Robert Greig
39  */

40 public class StandardAdminHandler implements AdminHandler {
41
42     private static final Object JavaDoc ksEntries = new Object JavaDoc();
43     
44     private static Runnable JavaDoc t9yBeforeRename;
45
46     /**
47      * Create or update the administration files for a particular file.
48      * This will create the CVS directory if necessary, and the
49      * Root and Repository files if necessary. It will also update
50      * the Entries file with the new entry
51      * @param localDirectory the local directory where the file in question
52      * lives (the absolute path). Must not end with a slash.
53      * @param entry the entry object for that file. If null, there is no
54      * entry to add, and the Entries file will not have any entries added to
55      * it (it will be created if it does not exist, however).
56      */

57     public void updateAdminData(String JavaDoc localDirectory, String JavaDoc repositoryPath,
58                                 Entry entry, GlobalOptions globalOptions)
59             throws IOException {
60         // add this directory to the list of those to check for emptiness if
61
// the prune option is specified
62

63         final File CVSdir = new File(localDirectory, "CVS"); //NOI18N
64

65         CVSdir.mkdirs();
66
67         // now ensure that the Root and Repository files exist
68
File rootFile = new File(CVSdir, "Root"); //NOI18N
69
if (!rootFile.exists()) {
70             PrintWriter w = new PrintWriter(new FileWriter(rootFile));
71             try {
72                 w.println(globalOptions.getCVSRoot());
73             } finally {
74                 w.close();
75             }
76         }
77         File repositoryFile = new File(CVSdir, "Repository"); //NOI18N
78
if (!repositoryFile.exists()) {
79             PrintWriter w = new PrintWriter(new FileWriter(repositoryFile));
80             try {
81                 if (entry != null && !entry.isDirectory()) {
82                     // If there is a file entry, the repository path is for a file!
83
int length = entry.getName().length();
84                     repositoryPath = repositoryPath.substring(0, repositoryPath.length() - length);
85                 }
86                 if (repositoryPath.endsWith("/")) { //NOI18N
87
repositoryPath = repositoryPath.substring(0,
88                                                               repositoryPath.length() - 1);
89                 }
90                 if (repositoryPath.length() == 0) {
91                     repositoryPath = "."; //NOI18N
92
}
93                 // we write out the relative path to the repository file
94
w.println(repositoryPath);
95             } finally {
96                 w.close();
97             }
98         }
99
100         final File entriesFile = new File(CVSdir, "Entries"); //NOI18N
101

102         // We assume that if we do not have an Entries file, we need to add
103
// the file to the parent CVS directory as well as create the
104
// Entries file for this directory
105
if (entriesFile.createNewFile()) {
106             // need to know if we had to create any directories so that we can
107
// update the CVS/Entries file in the *parent* director
108
addDirectoryToParentEntriesFile(CVSdir);
109
110             // We have created a new Entries file, so put a D in it to
111
// indicate that we understand directories.
112
// TODO: investigate what the point of this is. The command-line
113
// CVS client does it
114
final Writer w = new BufferedWriter(new FileWriter(entriesFile));
115             try {
116                 w.write("D"); //NOI18N
117
} finally {
118                 w.close();
119             }
120         }
121
122         // Update the Entries file
123
if (entry != null) {
124             updateEntriesFile(entriesFile, entry);
125         }
126     }
127
128     /**
129      * Simply delegates to File.exists(), does not provide any virtual files.
130      *
131      * @param file file to test for existence
132      * @return true if the file exists on disk, false otherwise
133      */

134     public boolean exists(File file) {
135         return file.exists();
136     }
137
138     /**
139      * Add a directory entry to the parent directory's Entries file, if it
140      * exists. Any line containing only a D is deleted from the parent
141      * Entries file.
142      * @param CVSdir the full path to the CVS directory for the directory
143      * that has just been added
144      */

145     private void addDirectoryToParentEntriesFile(File CVSdir)
146             throws IOException {
147         synchronized (ksEntries) {
148             File parentCVSEntries = seekEntries(CVSdir.getParentFile().getParentFile());
149
150             // only update if the file exists. The file will not exist in the
151
// case where this is the top level of the module
152
if (parentCVSEntries != null) {
153                 final File directory = parentCVSEntries.getParentFile();
154                 final File tempFile = new File(directory, "Entries.Backup"); //NOI18N
155
tempFile.createNewFile();
156                 BufferedReader reader = null;
157                 BufferedWriter writer = null;
158                 try {
159                     reader = new BufferedReader(new FileReader(parentCVSEntries));
160                     writer = new BufferedWriter(new FileWriter(tempFile));
161
162                     String JavaDoc line;
163
164                     // As in the updateEntriesFile method the new Entry
165
// only may be written, if it does not exist
166
boolean written = false;
167
168                     Entry directoryEntry = new Entry();
169                     directoryEntry.setName(CVSdir.getParentFile().getName());
170                     directoryEntry.setDirectory(true);
171
172                     while ((line = reader.readLine()) != null) {
173                         if (line.trim().equals("D")) { //NOI18N
174
// do not write out this line
175
continue;
176                         }
177
178                         final Entry currentEntry = new Entry(line);
179                         if (currentEntry.getName() != null
180                                 && currentEntry.getName().equals(directoryEntry.getName())) {
181                             writer.write(directoryEntry.toString());
182                             written = true;
183                         }
184                         else {
185                             writer.write(line);
186                         }
187                         writer.newLine();
188                     }
189
190                     if (!written) {
191                         writer.write(directoryEntry.toString());
192                         writer.newLine();
193                     }
194                 } finally {
195                     try {
196                         if (writer != null) {
197                             writer.close();
198                         }
199                     } finally {
200                         if (reader != null) {
201                             reader.close();
202                         }
203                     }
204                 }
205
206                 if (t9yBeforeRename != null) t9yBeforeRename.run();
207                 FileUtils.renameFile(tempFile, parentCVSEntries);
208             }
209         }
210     }
211
212     /**
213      * Update the specified Entries file with a given entry
214      * This method currently does the following, in order:
215      * <OL><LI>Create a new temporary file, Entries.temp</LI>
216      * <LI>Iterate through each line of the original file, checking
217      * if it matches the entry of the entry to be updated. If not, simply
218      * copy the line otherwise write the new entry and discard the old one</LI>
219      * <LI>Once all lines have been written, close both files</LI>
220      * <LI>Move the original file to Entries.temp2</LI>
221      * <LI>Move the file Entries.temp to Entries</LI>
222      * <LI>Delete Entries.temp2</LI></UL>
223      * Note that in the case where the Entries file does not exist, it
224      * is simply created and the entry appended immediately.
225      * This all ensures that if a failure occurs at any stage, the original
226      * Entries file can be retrieved
227      * @param originalFile the original Entries file, which need not exist yet
228      * @param entry the specific entry to update
229      * @throws IOException if an error occurs writing the files
230      */

231     private void updateEntriesFile(File originalFile, Entry entry)
232             throws IOException {
233         synchronized (ksEntries) {
234             final File directory = originalFile.getParentFile();
235             final File tempFile = new File(directory, "Entries.Backup"); //NOI18N
236
tempFile.createNewFile();
237
238             BufferedReader reader = null;
239             BufferedWriter writer = null;
240             try {
241                 reader = new BufferedReader(new FileReader(originalFile));
242                 writer = new BufferedWriter(new FileWriter(tempFile));
243                 String JavaDoc line;
244                 // indicates whether we have written the entry that was passed in
245
// if we finish copying the file without writing it, then it is
246
// a new entry and must simply be append at the end
247
boolean written = false;
248                 while ((line = reader.readLine()) != null) {
249                     final Entry currentEntry = new Entry(line);
250                     if ((currentEntry.getName() != null) &&
251                             currentEntry.getName().equals(entry.getName())) {
252                         writer.write(entry.toString());
253                         written = true;
254                     }
255                     else {
256                         writer.write(line);
257                     }
258                     writer.newLine();
259                 }
260                 if (!written) {
261                     writer.write(entry.toString());
262                     writer.newLine();
263                 }
264             } finally {
265                 try {
266                     if (writer != null) {
267                         writer.close();
268                     }
269                 } finally {
270                     if (reader != null) {
271                         reader.close();
272                     }
273                 }
274             }
275
276             if (t9yBeforeRename != null) t9yBeforeRename.run();
277             FileUtils.renameFile(tempFile, originalFile);
278         }
279     }
280
281     /**
282      * Get the Entry for the specified file, if one exists
283      * @param f the file
284      * @throws IOException if the Entries file cannot be read
285      */

286     public Entry getEntry(File file) throws IOException {
287         final File entriesFile = seekEntries(file.getParentFile()); //NOI18N
288
// if there is no Entries file we cannot very well get any Entry
289
// from it
290
if (entriesFile == null) {
291             return null;
292         }
293
294         processEntriesDotLog(new File(file.getParent(), "CVS")); //NOI18N
295

296         BufferedReader reader = null;
297         Entry entry = null;
298         boolean found = false;
299         try {
300             reader = new BufferedReader(new FileReader(entriesFile));
301             String JavaDoc line;
302             while (!found && ((line = reader.readLine()) != null)) {
303                 entry = new Entry(line);
304                 // can have a name of null in the case of the single
305
// D entry line spec, indicating no subdirectories
306
if (entry.getName() != null) {
307                     // file equality and string equality are not the same thing
308
File entryFile = new File(file.getParentFile(), entry.getName());
309                     found = entryFile.equals(file);
310                 }
311             }
312         }
313         finally {
314             if (reader != null) {
315                 reader.close();
316             }
317         }
318
319         if (!found) {
320             return null;
321         }
322
323         return entry;
324     }
325
326     /**
327      * Get the entries for a specified directory.
328      * @param directory the directory for which to get the entries
329      * @return an array of Entry objects
330      */

331     public Entry[] getEntriesAsArray(File directory)
332             throws IOException {
333         List entries = new LinkedList();
334
335         final File entriesFile = seekEntries(directory);
336         // if there is no Entries file we just return the empty iterator
337
if (entriesFile == null) {
338             return new Entry[0];
339         }
340
341         processEntriesDotLog(new File(directory, "CVS")); //NOI18N
342

343         BufferedReader reader = null;
344         Entry entry = null;
345         try {
346             reader = new BufferedReader(new FileReader(entriesFile));
347             String JavaDoc line;
348             while ((line = reader.readLine()) != null) {
349                 entry = new Entry(line);
350                 // can have a name of null in the case of the single
351
// D entry line spec, indicating no subdirectories
352
if (entry.getName() != null) {
353                     entries.add(entry);
354                 }
355             }
356         }
357         finally {
358             if (reader != null) {
359                 reader.close();
360             }
361         }
362         Entry[] toReturnArray = new Entry[entries.size()];
363         toReturnArray = (Entry[])entries.toArray(toReturnArray);
364         return toReturnArray;
365     }
366
367     /**
368      * Get the entries for a specified directory.
369      * @param directory the directory for which to get the entries (CVS/Entries is appended)
370      * @return an iterator of Entry objects
371      */

372     public Iterator getEntries(File directory)
373             throws IOException {
374         List entries = new LinkedList();
375
376         final File entriesFile = seekEntries(directory);
377         // if there is no Entries file we just return the empty iterator
378
if (entriesFile == null) {
379             return entries.iterator();
380         }
381
382         processEntriesDotLog(new File(directory, "CVS")); //NOI18N
383

384         BufferedReader reader = null;
385         Entry entry = null;
386         try {
387             reader = new BufferedReader(new FileReader(entriesFile));
388             String JavaDoc line;
389             while ((line = reader.readLine()) != null) {
390                 entry = new Entry(line);
391                 // can have a name of null in the case of the single
392
// D entry line spec, indicating no subdirectories
393
if (entry.getName() != null) {
394                     entries.add(entry);
395                 }
396             }
397         }
398         finally {
399             if (reader != null) {
400                 reader.close();
401             }
402         }
403         return entries.iterator();
404     }
405
406     /**
407      * Set the Entry for the specified file
408      * @param f the file whose entry is being updated
409      * @throws IOException if an error occurs writing the details
410      */

411     public void setEntry(File file, Entry entry)
412             throws IOException {
413         String JavaDoc parent = file.getParent();
414         File entriesFile = seekEntries(parent);
415         if (entriesFile == null) {
416             entriesFile = new File(parent, "CVS/Entries"); //NOI18N
417
}
418         processEntriesDotLog(new File(parent, "CVS")); //NOI18N
419
updateEntriesFile(entriesFile, entry);
420     }
421
422     /**
423      * Remove the Entry for the specified file
424      * @param f the file whose entry is to be removed
425      * @throws IOException if an error occurs writing the Entries file
426      */

427     public void removeEntry(File file) throws IOException {
428         synchronized (ksEntries) {
429             final File entriesFile = seekEntries(file.getParent());
430
431             // if there is no Entries file we cannot very well remove an Entry
432
// from it
433
if (entriesFile == null) {
434                 return;
435             }
436
437             processEntriesDotLog(new File(file.getParent(), "CVS")); //NOI18N
438

439             final File directory = file.getParentFile();
440             final File tempFile = new File(directory, "Entries.Backup"); //NOI18N
441
tempFile.createNewFile();
442             BufferedReader reader = null;
443             BufferedWriter writer = null;
444             try {
445                 reader = new BufferedReader(new FileReader(entriesFile));
446                 writer = new BufferedWriter(new FileWriter(tempFile));
447                 String JavaDoc line;
448                 // allows us to determine whether we need to put in a "D" line. We do
449
// that if we remove the last directory from the Entries file
450
boolean directoriesExist = false;
451                 while ((line = reader.readLine()) != null) {
452                     final Entry currentEntry = new Entry(line);
453                     if ((currentEntry.getName() != null) &&
454
455                             !currentEntry.getName().equals(file.getName())) {
456                         writer.write(currentEntry.toString());
457                         writer.newLine();
458                         directoriesExist = directoriesExist ||
459                                 currentEntry.isDirectory();
460                     }
461                 }
462                 if (!directoriesExist) {
463                     writer.write("D"); //NOI18N
464
writer.newLine();
465                 }
466             } finally {
467                 try {
468                     if (writer != null) {
469                         writer.close();
470                     }
471                 } finally {
472                     if (reader != null) {
473                         reader.close();
474                     }
475                 }
476             }
477
478             if (t9yBeforeRename != null) t9yBeforeRename.run();
479             FileUtils.renameFile(tempFile, entriesFile);
480         }
481     }
482
483     /**
484      * Get the repository path for a given directory, for example in
485      * the directory /home/project/foo/bar, the repository directory
486      * might be /usr/cvs/foo/bar. The repository directory is commonly
487      * stored in the file <pre>Repository</pre> in the CVS directory on
488      * the client. (This is the case in the standard CVS command-line tool).
489      * However, the path stored in that file is relative to the repository
490      * path
491      * @param directory the directory
492      * @param the repository path on the server, e.g. /home/bob/cvs. Must not
493      * end with a slash.
494      */

495     public String JavaDoc getRepositoryForDirectory(String JavaDoc directory,
496                                             String JavaDoc repository)
497             throws IOException {
498         // if there is no "CVS/Repository" file, try to search up the file-
499
// hierarchy
500
File repositoryFile = null;
501         String JavaDoc repositoryDirs = ""; //NOI18N
502
File dirFile = new File(directory);
503         while (true) {
504             // if there is no Repository file we cannot very well get any
505
// repository from it
506
if (dirFile == null
507                     || dirFile.getName().length() == 0
508                     || !dirFile.exists()) {
509                 throw new FileNotFoundException("Repository file not found " + //NOI18N
510
"for directory " + directory); //NOI18N
511
}
512
513             repositoryFile = new File(dirFile, "CVS/Repository"); //NOI18N
514
if (repositoryFile.exists()) {
515                 break;
516             }
517             repositoryDirs = '/' + dirFile.getName() + repositoryDirs;
518             dirFile = dirFile.getParentFile();
519         }
520         BufferedReader reader = null;
521         // fileRepository is the value of the repository read from the
522
// Repository file
523
String JavaDoc fileRepository = null;
524         try {
525             reader = new BufferedReader(new FileReader(repositoryFile));
526             fileRepository = reader.readLine();
527         }
528         finally {
529             if (reader != null) {
530                 reader.close();
531             }
532         }
533
534         if (fileRepository == null) {
535             fileRepository = ""; //NOI18N
536
}
537
538         fileRepository += repositoryDirs;
539         // absolute repository path ?
540
if (fileRepository.startsWith("/")) { //NOI18N
541
return fileRepository;
542         }
543
544         // #69795 'normalize' repository path
545
if (fileRepository.startsWith("./")) { // NOI18N
546
fileRepository = fileRepository.substring(2);
547         }
548         // otherwise the cvs is using relative repository path
549
// must be a forward slash, regardless of the local filing system
550
return repository + '/' + fileRepository;
551     }
552
553     /**
554      * Update the Entries file using information in the Entries.Log file
555      * (if present). If Entries.Log is not present, this method does
556      * nothing.
557      * @param directory the directory that contains the Entries file
558      * @throws IOException if an error occurs reading or writing the files
559      */

560     private void processEntriesDotLog(File directory) throws IOException {
561         
562         synchronized (ksEntries) {
563             final File entriesDotLogFile = new File(directory, "Entries.Log"); //NOI18N
564
if (!entriesDotLogFile.exists()) {
565                 return;
566             }
567
568             BufferedReader reader = new BufferedReader(new FileReader(
569                     entriesDotLogFile));
570
571             // make up a list of changes to be made based on what is in
572
// the .log file. Then apply them all later
573
List additionsList = new LinkedList();
574             HashSet removalSet = new HashSet();
575
576             String JavaDoc line;
577
578             try {
579                 while ((line = reader.readLine()) != null) {
580                     if (line.startsWith("A ")) { //NOI18N
581
final Entry entry = new Entry(line.substring(2));
582                         additionsList.add(entry);
583                     }
584                     else if (line.startsWith("R ")) { //NOI18N
585
final Entry entry = new Entry(line.substring(2));
586                         removalSet.add(entry.getName());
587                     }
588                     // otherwise ignore the line since we don't understand it
589
}
590             } finally {
591                 reader.close();
592             }
593
594             if ((additionsList.size() > 0) || (removalSet.size() > 0)) {
595                 final File backup = new File(directory, "Entries.Backup"); //NOI18N
596
final BufferedWriter writer = new BufferedWriter(new FileWriter(
597                         backup));
598                 final File entriesFile = new File(directory, "Entries"); //NOI18N
599
reader = new BufferedReader(new FileReader(entriesFile));
600
601                 try {
602                     // maintain a count of the number of directories so that
603
// we know whether to write the "D" line
604
int directoryCount = 0;
605
606                     while ((line = reader.readLine()) != null) {
607                         // we will write out the directory "understanding" line
608
// later, if necessary
609
if (line.trim().equals("D")) { //NOI18N
610
continue;
611                         }
612
613                         final Entry entry = new Entry(line);
614
615                         if (entry.isDirectory()) {
616                             directoryCount++;
617                         }
618
619                         if (!removalSet.contains(entry.getName())) {
620                             writer.write(entry.toString());
621                             writer.newLine();
622                             if (entry.isDirectory()) {
623                                 directoryCount--;
624                             }
625                         }
626                     }
627                     Iterator it = additionsList.iterator();
628                     while (it.hasNext()) {
629                         final Entry entry = (Entry)it.next();
630                         if (entry.isDirectory()) {
631                             directoryCount++;
632                         }
633                         writer.write(entry.toString());
634                         writer.newLine();
635                     }
636                     if (directoryCount == 0) {
637                         writer.write("D"); //NOI18N
638
writer.newLine();
639                     }
640                 } finally {
641                     try {
642                         reader.close();
643                     } finally {
644                         writer.close();
645                     }
646                 }
647
648                 if (t9yBeforeRename != null) t9yBeforeRename.run();
649                 FileUtils.renameFile(backup, entriesFile);
650             }
651             entriesDotLogFile.delete();
652         }
653     }
654
655     /**
656      * Get all the files contained within a given
657      * directory that are <b>known to CVS</b>.
658      * @param directory the directory to look in
659      * @return a set of all files.
660      */

661     public Set getAllFiles(File directory) throws IOException {
662         TreeSet fileSet = new TreeSet();
663         BufferedReader reader = null;
664         try {
665             final File entriesFile = seekEntries(directory); //NOI18N
666

667             // if for any reason we don't have an Entries file just return
668
// with the empty set.
669
if (entriesFile != null) {
670                 return fileSet; // Premature return
671
}
672
673             reader = new BufferedReader(new FileReader(entriesFile));
674             String JavaDoc line;
675
676             while ((line = reader.readLine()) != null) {
677                 final Entry entry = new Entry(line);
678
679                 if (entry.getName() != null) {
680                     final File f = new File(directory, entry.getName());
681                     if (f.isFile()) {
682                         fileSet.add(f);
683                     }
684                 }
685             }
686         }
687         finally {
688             if (reader != null) {
689                 reader.close();
690             }
691         }
692
693         return fileSet;
694     }
695
696     // XXX where is pairing setStickyTagForDirectory?
697
/**
698      * Checks for presence of CVS/Tag file and returns it's value.
699      * @return the value of CVS/Tag file for the specified directory
700      * null if file doesn't exist
701      */

702     public String JavaDoc getStickyTagForDirectory(File directory) {
703         BufferedReader reader = null;
704         File tagFile = new File(directory, "CVS/Tag"); //NOI18N
705

706         try {
707             reader = new BufferedReader(new FileReader(tagFile));
708             String JavaDoc tag = reader.readLine();
709             return tag;
710         }
711         catch (IOException ex) {
712             // silently ignore??
713
}
714         finally {
715             if (reader != null) {
716                 try {
717                     reader.close();
718                 }
719                 catch (IOException ex) {
720                     // silently ignore
721
}
722             }
723         }
724         return null;
725     }
726
727     /**
728      * If Entries do not exist restore them from backup.
729      *
730      * @param folder path where to seek CVS/Entries
731      * @return CVS/Entries file or null
732      */

733     private static File seekEntries(File folder) {
734         synchronized(ksEntries) {
735             File entries = new File(folder, "CVS/Entries"); // NOI18N
736
if (entries.exists()) {
737                 return entries;
738             } else {
739                 File backup = new File(folder, "CVS/Entries.Backup"); // NOI18N
740
if (backup.exists()) {
741                     try {
742                         if (t9yBeforeRename != null) t9yBeforeRename.run();
743                         FileUtils.renameFile(backup, entries);
744                         return entries;
745                     } catch (IOException e) {
746                         // ignore
747
}
748                 }
749             }
750             return null;
751         }
752     }
753
754     private static File seekEntries(String JavaDoc folder) {
755         return seekEntries(new File(folder));
756     }
757     
758     static void t9yBeforeRenameSync(Runnable JavaDoc run) {
759         t9yBeforeRename = run;
760     }
761 }
762
Popular Tags