KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > subversion > Subversion


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 NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.subversion;
21
22 import org.netbeans.modules.subversion.config.SvnConfigFiles;
23 import org.netbeans.modules.subversion.util.Context;
24 import org.netbeans.modules.subversion.client.*;
25 import org.netbeans.modules.subversion.util.SvnUtils;
26 import org.openide.ErrorManager;
27 import org.openide.filesystems.FileObject;
28 import org.openide.filesystems.FileUtil;
29 import org.tigris.subversion.svnclientadapter.*;
30 import org.openide.util.RequestProcessor;
31 import java.io.*;
32 import java.util.*;
33 import java.beans.PropertyChangeListener JavaDoc;
34 import java.beans.PropertyChangeSupport JavaDoc;
35 import java.util.logging.Level JavaDoc;
36 import java.util.logging.Logger JavaDoc;
37 import org.netbeans.modules.subversion.ui.diff.Setup;
38 import org.netbeans.modules.subversion.ui.ignore.IgnoreAction;
39 import org.netbeans.modules.versioning.spi.VCSInterceptor;
40 import org.netbeans.modules.versioning.spi.OriginalContent;
41 import org.netbeans.modules.versioning.util.VersioningListener;
42 import org.netbeans.modules.versioning.util.VersioningEvent;
43 import org.netbeans.modules.versioning.VersioningManager;
44 import org.netbeans.api.queries.SharabilityQuery;
45 import org.netbeans.modules.subversion.ui.repository.RepositoryConnection;
46
47 /**
48  * A singleton Subversion manager class, center of Subversion module. Use {@link #getInstance()} to get access
49  * to Subversion module functionality.
50  *
51  * @author Maros Sandor
52  */

53 public class Subversion {
54
55     /**
56      * Fired when textual annotations and badges have changed. The NEW value is Set<File> of files that changed or NULL
57      * if all annotaions changed.
58      */

59     static final String JavaDoc PROP_ANNOTATIONS_CHANGED = "annotationsChanged";
60
61     static final String JavaDoc PROP_VERSIONED_FILES_CHANGED = "versionedFilesChanged";
62     
63     static final String JavaDoc INVALID_METADATA_MARKER = "invalid-metadata"; // NOI18N
64

65     private static final int STATUS_DIFFABLE =
66             FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY |
67             FileInformation.STATUS_VERSIONED_ADDEDLOCALLY |
68             FileInformation.STATUS_VERSIONED_UPTODATE |
69             FileInformation.STATUS_VERSIONED_MODIFIEDLOCALLY |
70             FileInformation.STATUS_VERSIONED_MODIFIEDINREPOSITORY |
71             FileInformation.STATUS_VERSIONED_CONFLICT |
72             FileInformation.STATUS_VERSIONED_MERGE |
73             FileInformation.STATUS_VERSIONED_REMOVEDINREPOSITORY |
74             FileInformation.STATUS_VERSIONED_MODIFIEDINREPOSITORY |
75             FileInformation.STATUS_VERSIONED_MODIFIEDINREPOSITORY;
76         
77     private static Subversion instance;
78     
79     private FileStatusCache fileStatusCache;
80     private FilesystemHandler filesystemHandler;
81     private FileStatusProvider fileStatusProvider;
82     private Annotator annotator;
83     private HashMap<String JavaDoc, RequestProcessor> processorsToUrl;
84
85     private SvnClient noUrlClientWithoutListeners;
86     private SvnClient noUrlClientWithListeners;
87     
88     private final PropertyChangeSupport JavaDoc support = new PropertyChangeSupport JavaDoc(this);
89
90     public static synchronized Subversion getInstance() {
91         if (instance == null) {
92             instance = new Subversion();
93             instance.init();
94         }
95         return instance;
96     }
97
98     private Subversion() {
99     }
100     
101     private void init() {
102         Diagnostics.init();
103         loadIniParserClassesWorkaround();
104         setupSvnClientFactory();
105         
106         fileStatusCache = new FileStatusCache();
107         annotator = new Annotator(this);
108         fileStatusProvider = new FileStatusProvider();
109         filesystemHandler = new FilesystemHandler(this);
110         cleanup();
111     }
112     
113     public static void setupSvnClientFactory() {
114         try {
115             SvnClientFactory.getInstance().setup();
116         } catch (SVNClientException ex) {
117             Logger.getLogger("org.netbeans.modules.subversion").log(Level.INFO, UnsupportedSvnClientAdapter.getMessage());
118             
119             // ErrorManager.getDefault().annotate(ex, UnsupportedSvnClientAdapter.getMessage());
120
// ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
121
// TODO: #78951 hotfix
122
// ErrorManager.getDefault().notify(ErrorManager.USER, ex);
123
}
124     }
125             
126     /**
127      * Ini4j uses context classloader to load classes, use this as a workaround.
128      */

129     private void loadIniParserClassesWorkaround() {
130         ClassLoader JavaDoc cl = Thread.currentThread().getContextClassLoader();
131         Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
132         try {
133             SvnConfigFiles.getInstance(); // triggers ini4j initialization
134
} finally {
135             Thread.currentThread().setContextClassLoader(cl);
136         }
137     }
138
139     private void cleanup() {
140         getRequestProcessor().post(new Runnable JavaDoc() {
141             public void run() {
142                 try {
143                     Diagnostics.println("Cleaning up"); // NOI18N
144
fileStatusCache.cleanUp();
145                     // TODO: refresh all annotations
146
} finally {
147                     Diagnostics.println("END Cleaning up"); // NOI18N
148
}
149             }
150         }, 3000);
151     }
152     
153     public void shutdown() {
154         fileStatusProvider.shutdown();
155         // TODO: refresh all annotations
156
}
157
158     public SvnFileNode [] getNodes(Context context, int includeStatus) {
159         File [] files = fileStatusCache.listFiles(context, includeStatus);
160         SvnFileNode [] nodes = new SvnFileNode[files.length];
161         for (int i = 0; i < files.length; i++) {
162             nodes[i] = new SvnFileNode(files[i]);
163         }
164         return nodes;
165     }
166
167     /**
168      * Reads the svn:mime-type property or uses content analysis for unversioned files.
169      *
170      * @param file file to examine
171      * @return String mime type of the file (or best guess)
172      */

173     public String JavaDoc getMimeType(File file) {
174         FileObject fo = FileUtil.toFileObject(file);
175         String JavaDoc foMime;
176         if (fo == null) {
177             foMime = "content/unknown";
178         } else {
179             foMime = fo.getMIMEType();
180             if ("content/unknown".equals(foMime)) {
181                 foMime = "text/plain";
182             }
183         }
184         if ((fileStatusCache.getStatus(file).getStatus() & FileInformation.STATUS_VERSIONED) == 0) {
185             return SvnUtils.isFileContentBinary(file) ? "application/octet-stream" : foMime;
186         } else {
187             PropertiesClient client = new PropertiesClient(file);
188             try {
189                 byte [] mimeProperty = client.getProperties().get("svn:mime-type");
190                 if (mimeProperty == null) return foMime;
191                 return new String JavaDoc(mimeProperty);
192             } catch (IOException e) {
193                 return foMime;
194             }
195         }
196     }
197
198     /**
199      * Tests <tt>.svn</tt> directory itself.
200      */

201     public boolean isAdministrative(File file) {
202         String JavaDoc name = file.getName();
203         return isAdministrative(name) && file.isDirectory();
204     }
205
206     public boolean isAdministrative(String JavaDoc fileName) {
207         return fileName.equals(".svn") || fileName.equals("_svn"); // NOI18N
208
}
209     
210     public FileStatusCache getStatusCache() {
211         return fileStatusCache;
212     }
213
214     public Annotator getAnnotator() {
215         return annotator;
216     }
217
218     public SvnClient getClient(SVNUrl repositoryUrl,
219                                String JavaDoc username,
220                                String JavaDoc password)
221     throws SVNClientException
222     {
223         return getClient(repositoryUrl, username, password, SvnClientExceptionHandler.EX_DEFAULT_HANDLED_EXCEPTIONS);
224     }
225     
226     public SvnClient getClient(SVNUrl repositoryUrl,
227                                String JavaDoc username,
228                                String JavaDoc password,
229                                int handledExceptions) throws SVNClientException {
230         SvnClient client = SvnClientFactory.getInstance().createSvnClient(repositoryUrl, null, username, password, handledExceptions);
231         attachListeners(client);
232         return client;
233     }
234     
235     public SvnClient getClient(SVNUrl repositoryUrl, SvnProgressSupport support) throws SVNClientException {
236         String JavaDoc username = ""; // NOI18N
237
String JavaDoc password = ""; // NOI18N
238
RepositoryConnection rc = SvnModuleConfig.getDefault().getRepositoryConnection(repositoryUrl.toString());
239         if(rc != null) {
240             username = rc.getUsername();
241             password = rc.getPassword();
242         }
243         SvnClient client = SvnClientFactory.getInstance().createSvnClient(repositoryUrl, support, /*null, */username, password, SvnClientExceptionHandler.EX_DEFAULT_HANDLED_EXCEPTIONS);
244         attachListeners(client);
245         return client;
246     }
247     
248     public SvnClient getClient(File file) throws SVNClientException {
249         return getClient(file, null);
250     }
251
252     public SvnClient getClient(File file, SvnProgressSupport support) throws SVNClientException {
253         SVNUrl repositoryUrl = SvnUtils.getRepositoryRootUrl(file);
254         assert repositoryUrl != null : "Unable to get repository: " + file.getAbsolutePath() + " is probably unmanaged."; // NOI18N
255

256         return getClient(repositoryUrl, support);
257     }
258     
259     public SvnClient getClient(Context ctx, SvnProgressSupport support) throws SVNClientException {
260         File[] roots = ctx.getRootFiles();
261         SVNUrl repositoryUrl = null;
262         for (int i = 0; i<roots.length; i++) {
263              repositoryUrl = SvnUtils.getRepositoryRootUrl(roots[0]);
264             if (repositoryUrl != null) {
265                 break;
266             }
267         }
268
269         assert repositoryUrl != null : "Unable to get repository, context contains only unmanaged files!"; // NOI18N
270

271         return getClient(repositoryUrl, support);
272     }
273     
274     public SvnClient getClient(SVNUrl repositoryUrl) throws SVNClientException {
275         return getClient(repositoryUrl, null);
276     }
277     
278     /**
279      * <b>Creates</b> ClientAtapter implementation that already handles:
280      * <ul>
281      * <li>prompts user for password if necessary,
282      * <li>let user specify proxy setting on network errors or
283      * <li>let user cancel operation
284      * <li>logs command execuion into output tab
285      * <li>posts notification events in status cache
286      * </ul>
287      *
288      * <p>It hanldes cancellability
289      */

290     public SvnClient getClient(boolean attachListeners) {
291         cleanupFilesystem();
292         if(attachListeners) {
293             if(noUrlClientWithListeners == null) {
294                 noUrlClientWithListeners = SvnClientFactory.getInstance().createSvnClient();
295                 attachListeners(noUrlClientWithListeners);
296             }
297             return noUrlClientWithListeners;
298         } else {
299             if(noUrlClientWithoutListeners == null) {
300                 noUrlClientWithoutListeners = SvnClientFactory.getInstance().createSvnClient();
301             }
302             return noUrlClientWithoutListeners;
303         }
304     }
305     
306     public FilesystemHandler getFileSystemHandler() {
307         return filesystemHandler;
308     }
309
310     /**
311      * Tests whether a file or directory should receive the STATUS_NOTVERSIONED_NOTMANAGED status.
312      * All files and folders that have a parent with CVS/Repository file are considered versioned.
313      *
314      * @param file a file or directory
315      * @return false if the file should receive the STATUS_NOTVERSIONED_NOTMANAGED status, true otherwise
316      */

317     public boolean isManaged(File file) {
318         return VersioningManager.getInstance().getOwner(file) instanceof SubversionVCS && !SvnUtils.isPartOfSubversionMetadata(file);
319     }
320
321     public void versionedFilesChanged() {
322         support.firePropertyChange(PROP_VERSIONED_FILES_CHANGED, null, null);
323     }
324     
325     /**
326      * Tests whether the file is managed by this versioning system. If it is, the method should return the topmost
327      * parent of the file that is still versioned.
328      *
329      * @param file a file
330      * @return File the file itself or one of its parents or null if the supplied file is NOT managed by this versioning system
331      */

332     File getTopmostManagedParent(File file) {
333         if (SvnUtils.isPartOfSubversionMetadata(file)) {
334             for (;file != null; file = file.getParentFile()) {
335                 if (isAdministrative(file)) {
336                     file = file.getParentFile();
337                     break;
338                 }
339             }
340         }
341         File topmost = null;
342         for (; file != null; file = file.getParentFile()) {
343             if (new File(file, ".svn/entries").canRead() || new File(file, "_svn/entries").canRead()) { // NOI18N
344
topmost = file;
345             }
346         }
347         return topmost;
348     }
349     
350     /**
351      * TODO: Backdoor for SvnClientFactory
352      */

353     public void cleanupFilesystem() {
354         filesystemHandler.removeInvalidMetadata();
355     }
356
357     private void attachListeners(SvnClient client) {
358         client.addNotifyListener(getLogger(client.getSvnUrl()));
359         client.addNotifyListener(fileStatusCache);
360     }
361
362     /**
363      *
364      * @param repositoryRoot URL of Subversion repository so that logger writes to correct output tab. Can be null
365      * in which case the logger will not print anything
366      * @return OutputLogger logger to write to
367      */

368     public OutputLogger getLogger(SVNUrl repositoryRoot) {
369         return OutputLogger.getLogger(repositoryRoot);
370     }
371
372     /**
373      * Non-recursive ignore check.
374      *
375      * <p>Side effect: if under SVN version control
376      * it sets svn:ignore property
377      *
378      * @return true if file is listed in parent's ignore list
379      * or IDE thinks it should be.
380      */

381     boolean isIgnored(final File file) {
382         String JavaDoc name = file.getName();
383
384         // ask SVN
385

386         final File parent = file.getParentFile();
387         if (parent != null) {
388             int pstatus = fileStatusCache.getStatus(parent).getStatus();
389             if ((pstatus & FileInformation.STATUS_VERSIONED) != 0) {
390                 try {
391                     SvnClient client = getClient(false);
392                     
393                     List<String JavaDoc> patterns = client.getIgnoredPatterns(parent);
394                     List<String JavaDoc> gignores = SvnConfigFiles.getInstance().getGlobalIgnores();
395                     // merge global ignores and ignore patterns
396
for (Iterator<String JavaDoc> it = gignores.iterator(); it.hasNext(); patterns.add(it.next()));
397
398                     if(SvnUtils.getMatchinIgnoreParterns(patterns,name, true).size() > 0) {
399                         return true;
400                     }
401                     
402                 } catch (SVNClientException ex) {
403                     ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
404                 }
405             }
406         }
407
408         if (SharabilityQuery.getSharability(file) == SharabilityQuery.NOT_SHARABLE) {
409             try {
410                 // BEWARE: In NetBeans VISIBILTY == SHARABILITY ... and we hide Locally Removed folders => we must not Ignore them by mistake
411
FileInformation info = fileStatusCache.getCachedStatus(file); // getStatus may cause stack overflow
412
if (SubversionVisibilityQuery.isHiddenFolder(info, file)) {
413                     return false;
414                 }
415                 // if IDE-ignore-root then propagate IDE opinion to Subversion svn:ignore
416
if (SharabilityQuery.getSharability(parent) != SharabilityQuery.NOT_SHARABLE) {
417                     if ((fileStatusCache.getStatus(parent).getStatus() & FileInformation.STATUS_VERSIONED) != 0) {
418                         IgnoreAction.ignore(file);
419                     }
420                 }
421             } catch (SVNClientException ex) {
422                 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
423             }
424             return true;
425         } else {
426             // backward compatability #68124
427
if (".nbintdb".equals(name)) { // NOI18N
428
return true;
429             }
430
431             return false;
432         }
433     }
434
435     /**
436      * Serializes all SVN requests (moves them out of AWT).
437      */

438     public RequestProcessor getRequestProcessor() {
439         return getRequestProcessor(null);
440     }
441
442     /**
443      * Serializes all SVN requests (moves them out of AWT).
444      */

445     public RequestProcessor getRequestProcessor(SVNUrl url) {
446         if(processorsToUrl == null) {
447             processorsToUrl = new HashMap<String JavaDoc, RequestProcessor>();
448         }
449
450         String JavaDoc key;
451         if(url != null) {
452             key = url.toString();
453         } else {
454             key = "ANY_URL"; // NOI18N
455
}
456
457         RequestProcessor rp = processorsToUrl.get(key);
458         if(rp == null) {
459             rp = new RequestProcessor("Subversion - " + key, 1, true); // NOI18N
460
processorsToUrl.put(key, rp);
461         }
462         return rp;
463     }
464
465     FileStatusProvider getVCSAnnotator() {
466         return fileStatusProvider;
467     }
468
469     VCSInterceptor getVCSInterceptor() {
470         return filesystemHandler;
471     }
472
473     /**
474      * Refreshes all textual annotations and badges.
475      */

476     public void refreshAllAnnotations() {
477         support.firePropertyChange(PROP_ANNOTATIONS_CHANGED, null, null);
478     }
479
480     void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
481         support.addPropertyChangeListener(listener);
482     }
483
484     void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
485         support.removePropertyChangeListener(listener);
486     }
487
488     public OriginalContent getVCSOriginalContent(File file) {
489         FileInformation info = fileStatusCache.getStatus(file);
490         if ((info.getStatus() & STATUS_DIFFABLE) == 0) return null;
491         return new SubversionOriginalContent(file);
492     }
493     
494     
495     private class SubversionOriginalContent extends OriginalContent implements VersioningListener {
496         
497         public SubversionOriginalContent(File working) {
498             super(working);
499         }
500
501         protected void getOriginalFiles(File destination, Set<File> files) throws Exception JavaDoc {
502             for (File file : files) {
503                 File original = VersionsCache.getInstance().getFileRevision(file, Setup.REVISION_BASE);
504                 if (original == null) throw new IOException("Unable to get BASE revision of " + file);
505
506                 File daoFile = new File(destination, file.getName());
507                 org.netbeans.modules.versioning.util.Utils.copyStreamsCloseAll(new FileOutputStream(daoFile), new FileInputStream(original));
508                 daoFile.deleteOnExit();
509             }
510         }
511
512         public void versioningEvent(VersioningEvent event) {
513             if (FileStatusCache.EVENT_FILE_STATUS_CHANGED == event.getId()) {
514                 File eventFile = (File) event.getParams()[0];
515                 if (eventFile.equals(workingCopy)) {
516                     support.firePropertyChange(PROP_CONTENT_CHANGED, null, null);
517                 }
518             }
519         }
520         
521         public void addPropertyChangeListener(PropertyChangeListener JavaDoc listener) {
522             if (!support.hasListeners(null)) {
523                 fileStatusCache.addVersioningListener(this);
524             }
525             super.addPropertyChangeListener(listener);
526         }
527
528         public void removePropertyChangeListener(PropertyChangeListener JavaDoc listener) {
529             super.removePropertyChangeListener(listener);
530             if (!support.hasListeners(null)) {
531                 fileStatusCache.removeVersioningListener(this);
532             }
533         }
534     }
535     
536 }
537
Popular Tags