001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.versioning.system.cvss;
043:
044: import org.netbeans.modules.versioning.util.ListenersSupport;
045: import org.netbeans.modules.versioning.util.VersioningListener;
046: import org.netbeans.lib.cvsclient.admin.AdminHandler;
047: import org.netbeans.lib.cvsclient.admin.Entry;
048: import org.netbeans.lib.cvsclient.command.*;
049: import org.netbeans.lib.cvsclient.command.add.AddCommand;
050: import org.netbeans.lib.cvsclient.connection.AuthenticationException;
051: import org.netbeans.lib.cvsclient.file.FileHandler;
052: import org.netbeans.lib.cvsclient.file.FileUtils;
053: import org.netbeans.modules.versioning.system.cvss.util.Utils;
054: import org.netbeans.modules.versioning.system.cvss.util.Context;
055: import org.netbeans.modules.versioning.system.cvss.ui.syncview.CvsSynchronizeTopComponent;
056: import org.netbeans.modules.versioning.spi.VCSAnnotator;
057: import org.netbeans.modules.versioning.spi.VCSInterceptor;
058: import org.netbeans.modules.versioning.spi.VersioningSupport;
059: import org.netbeans.api.queries.SharabilityQuery;
060: import org.openide.ErrorManager;
061: import org.openide.util.RequestProcessor;
062: import org.openide.filesystems.*;
063:
064: import javax.swing.*;
065: import java.io.*;
066: import java.util.*;
067: import java.util.logging.Logger;
068: import java.util.logging.Level;
069: import java.util.regex.Pattern;
070: import java.util.regex.PatternSyntaxException;
071:
072: /**
073: * A singleton CVS manager class, center of CVS module. Use {@link #getInstance()} to get access
074: * to CVS module functionality.
075: *
076: * @author Maros Sandor
077: */
078: public class CvsVersioningSystem {
079:
080: private static CvsVersioningSystem instance;
081:
082: public static final String FILENAME_CVSIGNORE = ".cvsignore"; // NOI18N
083: public static final String FILENAME_CVS = "CVS"; // NOI18N
084:
085: public static final Object EVENT_PARAM_CHANGED = new Object();
086: public static final Object PARAM_BATCH_REFRESH_RUNNING = new Object();
087: public static final Object EVENT_VERSIONED_FILES_CHANGED = new Object();
088:
089: public static final Object EVENT_REFRESH_ANNOTATIONS = new Object();
090:
091: public static final String FILENAME_CVS_REPOSITORY = FILENAME_CVS
092: + "/Repository"; // NOI18N
093: public static final String FILENAME_CVS_ENTRIES = FILENAME_CVS
094: + "/Entries"; // NOI18N
095:
096: /**
097: * Extensions to be treated as text although MIME type may suggest otherwise.
098: */
099: private static final Set textExtensions = new HashSet(Arrays
100: .asList(new String[] { "txt", "xml", "html", "properties",
101: "mf", "jhm", "hs", "form" })); // NOI18N
102:
103: private final Map<String, ClientRuntime> clientsCache = new HashMap<String, ClientRuntime>();
104: private final Map params = new HashMap();
105:
106: private GlobalOptions defaultGlobalOptions;
107: private FileStatusCache fileStatusCache;
108:
109: private CvsLiteAdminHandler sah;
110: private CvsLiteFileHandler workdirFileHandler;
111: private CvsLiteGzippedFileHandler workdirGzippedFileHandler;
112: private FilesystemHandler filesystemHandler;
113: private VCSAnnotator fileStatusProvider;
114:
115: private Annotator annotator;
116:
117: private final Set<Pattern> userIgnorePatterns = new HashSet<Pattern>();
118: private boolean userIgnorePatternsReset;
119: private long userIgnorePatternsTimestamp;
120:
121: private final Set<File> unignoreOverride = new HashSet<File>(1);
122:
123: private final Set<File> alreadyGeneratedFiles = new HashSet<File>(5);
124:
125: public static synchronized CvsVersioningSystem getInstance() {
126: if (instance == null) {
127: instance = new CvsVersioningSystem();
128: instance.init();
129: }
130: return instance;
131: }
132:
133: private void init() {
134: defaultGlobalOptions = CvsVersioningSystem
135: .createGlobalOptions();
136: sah = new CvsLiteAdminHandler();
137: workdirFileHandler = new CvsLiteFileHandler();
138: FileUtils.setFileReadOnlyHandler(workdirFileHandler);
139: workdirGzippedFileHandler = new CvsLiteGzippedFileHandler();
140: fileStatusCache = new FileStatusCache(this );
141: filesystemHandler = new FilesystemHandler(this );
142: annotator = new Annotator(this );
143: fileStatusProvider = new FileStatusProvider();
144: }
145:
146: void shutdown() {
147: SwingUtilities.invokeLater(new Runnable() {
148: public void run() {
149: try {
150: CvsSynchronizeTopComponent.getInstance().close();
151: } catch (Throwable e) {
152: // ignore, this component is already invalid
153: }
154: }
155: });
156: }
157:
158: private CvsVersioningSystem() {
159: }
160:
161: public CvsFileTableModel getFileTableModel(Context context,
162: int displayStatuses) {
163: return new CvsFileTableModel(context, displayStatuses);
164: }
165:
166: /**
167: * Determines correct CVS client from the given cvs root.
168: *
169: * @param cvsRoot root never <code>null</code>
170: * @return
171: */
172: public ClientRuntime getClientRuntime(String cvsRoot) {
173:
174: cvsRoot.length(); // rise NPE
175:
176: ClientRuntime clientRuntime;
177: synchronized (clientsCache) {
178: clientRuntime = clientsCache.get(cvsRoot);
179: if (clientRuntime == null) {
180: clientRuntime = new ClientRuntime(cvsRoot);
181: clientsCache.put(cvsRoot, clientRuntime);
182: }
183: }
184: return clientRuntime;
185: }
186:
187: /**
188: * Determines CVS root for the given command.
189: *
190: * @param cmd a CVS command
191: * @return CVSRoot the command will execute in
192: * @throws NotVersionedException if the root cannot be determined (no CVS/Root file or unsupported command)
193: */
194: String detectCvsRoot(Command cmd) throws NotVersionedException {
195: File[] files;
196:
197: if (cmd instanceof AddCommand) {
198: AddCommand c = (AddCommand) cmd;
199: files = c.getFiles();
200: } else if (cmd instanceof BasicCommand) {
201: BasicCommand c = (BasicCommand) cmd;
202: files = c.getFiles();
203: } else {
204: throw new NotVersionedException(
205: "Cannot determine CVSRoot for command: " + cmd); // NOI18N
206: }
207:
208: File oneFile = files[0];
209: try {
210: return Utils.getCVSRootFor(oneFile);
211: } catch (IOException e) {
212: throw new NotVersionedException(
213: "Cannot determine CVSRoot for: " + oneFile); // NOI18N
214: }
215:
216: }
217:
218: /**
219: * Executes this command asynchronously, in a separate thread, and returns immediately. The command may
220: * or may not execute immediately, depending on previous commands sent to the CVS client that may be
221: * still waiting for execution.
222: *
223: * @param cmd command to execute
224: * @param mgr listener for events the command produces
225: * @throws CommandException
226: * @throws AuthenticationException
227: */
228: public RequestProcessor.Task post(Command cmd, ExecutorSupport mgr)
229: throws CommandException, AuthenticationException,
230: NotVersionedException, IllegalCommandException, IOException {
231: return post(cmd, defaultGlobalOptions, mgr);
232: }
233:
234: /**
235: * Schedules given command for execution.
236: * @param cmd
237: * @param options Global options to use, may be set to null to use default options
238: * @param mgr
239: * @return already scheduled task
240: * @throws IllegalCommandException if the command is not valid, e.g. it contains files that cannot be
241: * processed by a single command (they do not have a common filesystem root OR their CVS Roots differ)
242: */
243: public RequestProcessor.Task post(Command cmd,
244: GlobalOptions options, ExecutorSupport mgr)
245: throws IllegalCommandException {
246: ClientRuntime clientRuntime = getClientRuntime(cmd, options);
247: RequestProcessor.Task task = clientRuntime.createTask(cmd,
248: options != null ? options : defaultGlobalOptions, mgr);
249: task.schedule(0);
250: return task;
251: }
252:
253: /**
254: * Gets client runtime (a repository session).
255: *
256: * @return runtime never <code>null</code>
257: */
258: public ClientRuntime getClientRuntime(Command cmd,
259: GlobalOptions options) {
260: String root;
261: if (options != null && options.getCVSRoot() != null) {
262: root = options.getCVSRoot();
263: } else {
264: try {
265: root = detectCvsRoot(cmd);
266: } catch (NotVersionedException e) {
267: if (options == null)
268: return null;
269: root = options.getCVSRoot();
270: }
271: }
272: return getClientRuntime(root);
273: }
274:
275: public FileStatusCache getStatusCache() {
276: return fileStatusCache;
277: }
278:
279: ListenersSupport listenerSupport = new ListenersSupport(this );
280:
281: public void addVersioningListener(VersioningListener listener) {
282: listenerSupport.addListener(listener);
283: }
284:
285: public void removeVersioningListener(VersioningListener listener) {
286: listenerSupport.removeListener(listener);
287: }
288:
289: /**
290: * Returns true for files that were originally ignored by default (.exe files for example) but the user
291: * explicitly invoked CVS/Unignore on them.
292: *
293: * @param file a file to test
294: * @return true if the file was explicitly Unignored by user
295: */
296: boolean isUnignored(final File file) {
297: return unignoreOverride.contains(file);
298: }
299:
300: /**
301: * Checks if the file is ignored by CVS module. This method assumes that the file is managed so
302: * if you do not know this beforehand, you have to call isManaged() first.
303: *
304: * @param file file to be tested
305: * @return true, if the file is ignored by CVS, false otherwise.
306: */
307: boolean isIgnored(final File file) {
308: if (file.isDirectory()) {
309: File cvsRepository = new File(file, FILENAME_CVS_REPOSITORY);
310: if (cvsRepository.canRead())
311: return false;
312: }
313: String name = file.getName();
314:
315: // backward compatability #68124
316: if (".nbintdb".equals(name)) { // NOI18N
317: return true;
318: }
319:
320: if (isUnignored(file))
321: return false;
322:
323: Set<Pattern> patterns = new HashSet<Pattern>(Arrays
324: .asList(CvsModuleConfig.getDefault()
325: .getIgnoredFilePatterns()));
326: addUserPatterns(patterns);
327: addCvsIgnorePatterns(patterns, file.getParentFile());
328:
329: for (Iterator i = patterns.iterator(); i.hasNext();) {
330: Pattern pattern = (Pattern) i.next();
331: if (pattern.matcher(name).matches())
332: return true;
333: }
334:
335: // #67900 global sharability query will report .cvsignore as not sharable
336: if (FILENAME_CVSIGNORE.equals(name))
337: return false;
338:
339: int sharability = SharabilityQuery.getSharability(file);
340: if (sharability == SharabilityQuery.NOT_SHARABLE) {
341: // BEWARE: In NetBeans VISIBILTY == SHARABILITY ... and we hide Locally Removed folders => we must not Ignore them by mistake
342: if (CvsVisibilityQuery.isHiddenFolder(file)) {
343: return false;
344: }
345: // #90564: make sure that we only try to generate the .cvsignore file ONCE and if the user deletes it
346: // the file will NOT be re-generated. Do this only for auto-generated .cvsignore files.
347: File cvsIgnoreFile = new File(file.getParentFile(),
348: FILENAME_CVSIGNORE);
349: if (file.exists() && !cvsIgnoreFile.exists()) {
350: if (!alreadyGeneratedFiles.add(cvsIgnoreFile))
351: return true;
352: }
353: try {
354: setIgnored(file);
355: } catch (IOException e) {
356: // strange, but does no harm
357: }
358: return true;
359: } else {
360: return false;
361: }
362: }
363:
364: private void addUserPatterns(Set<Pattern> patterns) {
365: File userIgnores = new File(System.getProperty("user.home"),
366: FILENAME_CVSIGNORE); // NOI18N
367: long lm = userIgnores.lastModified();
368: if (lm > userIgnorePatternsTimestamp || lm == 0
369: && userIgnorePatternsTimestamp > 0) {
370: userIgnorePatternsTimestamp = lm;
371: parseUserPatterns(userIgnores);
372: }
373: if (userIgnorePatternsReset) {
374: patterns.clear();
375: }
376: patterns.addAll(userIgnorePatterns);
377: }
378:
379: private void parseUserPatterns(File userIgnores) {
380: userIgnorePatternsReset = false;
381: userIgnorePatterns.clear();
382: BufferedReader r = null;
383: try {
384: r = new BufferedReader(new FileReader(userIgnores));
385: String s;
386: while ((s = r.readLine()) != null) {
387: if ("!".equals(s)) { // NOI18N
388: userIgnorePatternsReset = true;
389: userIgnorePatterns.clear();
390: } else {
391: try {
392: userIgnorePatterns.add(sh2regex(s));
393: } catch (IOException e) {
394: // unsupported pattern
395: }
396: }
397: }
398: } catch (IOException e) {
399: // user has invalid ignore list, ignore it
400: } finally {
401: if (r != null)
402: try {
403: r.close();
404: } catch (IOException e) {
405: }
406: }
407: }
408:
409: /**
410: * Converts shell file pattern to regex pattern.
411: *
412: * @param s unix shell pattern
413: * @return regex patterm
414: * @throws IOException if this shell pattern is not supported
415: */
416: private static Pattern sh2regex(String s) throws IOException {
417: // TODO: implement full SH->REGEX convertor
418: s = s.replaceAll("\\.", "\\\\."); // NOI18N
419: s = s.replaceAll("\\*", ".*"); // NOI18N
420: s = s.replaceAll("\\?", "."); // NOI18N
421: try {
422: return Pattern.compile(s);
423: } catch (PatternSyntaxException e) {
424: throw new IOException(e.getMessage());
425: }
426: }
427:
428: /**
429: * Tests whether a file or directory should receive the STATUS_NOTVERSIONED_NOTMANAGED status.
430: * All files and folders that have a parent with CVS/Repository file are considered versioned.
431: *
432: * @param file a file or directory
433: * @return false if the file should receive the STATUS_NOTVERSIONED_NOTMANAGED status, true otherwise
434: */
435: boolean isManaged(File file) {
436: return VersioningSupport.getOwner(file) instanceof CVS
437: && !Utils.isPartOfCVSMetadata(file);
438: }
439:
440: public void versionedFilesChanged() {
441: listenerSupport
442: .fireVersioningEvent(EVENT_VERSIONED_FILES_CHANGED);
443: }
444:
445: /**
446: * Tests whether the file is managed by this versioning system. If it is, the method should return the topmost
447: * parent of the file that is still versioned.
448: *
449: * @param file a file
450: * @return File the file itself or one of its parents or null if the supplied file is NOT managed by this versioning system
451: */
452: File getTopmostManagedParent(File file) {
453: if (Utils.isPartOfCVSMetadata(file)) {
454: for (; file != null; file = file.getParentFile()) {
455: if (file.getName().equals(FILENAME_CVS)
456: && (file.isDirectory() || !file.exists())) {
457: file = file.getParentFile();
458: break;
459: }
460: }
461: }
462: File topmost = null;
463: for (; file != null; file = file.getParentFile()) {
464: if (org.netbeans.modules.versioning.util.Utils
465: .isScanForbidden(file))
466: break;
467: if (Utils.containsMetadata(file)) {
468: topmost = file;
469: }
470: }
471: return topmost;
472: }
473:
474: private void addCvsIgnorePatterns(Set<Pattern> patterns, File file) {
475: Set<String> shPatterns;
476: try {
477: shPatterns = readCvsIgnoreEntries(file);
478: } catch (IOException e) {
479: // ignore invalid entries
480: return;
481: }
482: for (Iterator i = shPatterns.iterator(); i.hasNext();) {
483: String shPattern = (String) i.next();
484: if ("!".equals(shPattern)) { // NOI18N
485: patterns.clear();
486: } else {
487: try {
488: patterns.add(sh2regex(shPattern));
489: } catch (IOException e) {
490: // unsupported pattern
491: }
492: }
493: }
494: }
495:
496: private boolean isInCvsIgnore(File file) {
497: try {
498: String patternToIgnore = computePatternToIgnore(file
499: .getName());
500: return readCvsIgnoreEntries(file.getParentFile()).contains(
501: patternToIgnore);
502: } catch (IOException e) {
503: ErrorManager.getDefault().notify(e);
504: return false;
505: }
506: }
507:
508: public boolean isIgnoredFilename(File file) {
509: if (FILENAME_CVS.equals(file.getName()))
510: return true;
511: return false;
512: }
513:
514: public AdminHandler getAdminHandler() {
515: return sah;
516: }
517:
518: public FileHandler getFileHandler() {
519: return workdirFileHandler;
520: }
521:
522: public FileHandler getGzippedFileHandler() {
523: return workdirGzippedFileHandler;
524: }
525:
526: public Annotator getAnnotator() {
527: return annotator;
528: }
529:
530: public Object getParameter(Object key) {
531: synchronized (params) {
532: return params.get(key);
533: }
534: }
535:
536: public KeywordSubstitutionOptions getDefaultKeywordSubstitution(
537: File file) {
538: // TODO: Let user configure defaults
539: return isText(file) || isBinary(file) == false ? KeywordSubstitutionOptions.DEFAULT
540: : KeywordSubstitutionOptions.BINARY;
541: }
542:
543: /**
544: * @return true if the file is almost certainly textual.
545: */
546: public boolean isText(File file) {
547: if (FILENAME_CVSIGNORE.equals(file.getName())) {
548: return true;
549: }
550: // honor Entries, only if this fails use MIME type, etc.
551: try {
552: Entry entry = sah.getEntry(file);
553: if (entry != null) {
554: return !entry.isBinary();
555: }
556: } catch (IOException e) {
557: // ignore, probably new or nonexistent file
558: }
559: if (org.netbeans.modules.versioning.util.Utils
560: .isFileContentText(file)) {
561: return true;
562: }
563:
564: // TODO: HACKS begin, still needed?
565: int idx = file.getName().lastIndexOf('.');
566: return idx != -1
567: && textExtensions.contains(file.getName().substring(
568: idx + 1));
569: }
570:
571: /**
572: * Uses first 1024 bytes test. A control byte means binary.
573: * @return true if the file is almost certainly binary.
574: */
575: public boolean isBinary(File file) {
576: InputStream in = null;
577: try {
578: in = new FileInputStream(file);
579: in = new BufferedInputStream(in);
580: for (int i = 0; i < 1024; i++) {
581: int ch = in.read();
582: if (ch == -1)
583: break;
584: if (ch < 32 && ch != '\t' && ch != '\n' && ch != '\r') {
585: return true;
586: }
587: }
588: } catch (IOException e) {
589: ErrorManager err = ErrorManager.getDefault();
590: err.notify(ErrorManager.INFORMATIONAL, e);
591: } finally {
592: if (in != null) {
593: try {
594: in.close();
595: } catch (IOException alreadyClosed) {
596: // ignore
597: }
598: }
599: }
600: return false;
601: }
602:
603: public void setParameter(Object key, Object value) {
604: Object old;
605: synchronized (params) {
606: old = params.put(key, value);
607: }
608: if (old != value)
609: listenerSupport.fireVersioningEvent(EVENT_PARAM_CHANGED,
610: key);
611: }
612:
613: /**
614: * Adds all supplied files to 'cvsignore' file. They need not reside in the same folder.
615: *
616: * @param files files to ignore
617: */
618: public void setIgnored(File[] files) {
619: for (int i = 0; i < files.length; i++) {
620: try {
621: setIgnored(files[i]);
622: } catch (IOException e) {
623: ErrorManager.getDefault().notify(e);
624: }
625: }
626: }
627:
628: /**
629: * Adds supplied file to 'cvsignore' file.
630: *
631: * @param file file to ignore
632: */
633: private void setIgnored(File file) throws IOException {
634: if (file.exists()) {
635: addToCvsIgnore(file);
636: }
637: }
638:
639: void setNotUnignored(File file) {
640: unignoreOverride.remove(file);
641: }
642:
643: public void setNotignored(File[] files) {
644: for (File file : files) {
645: if (isInCvsIgnore(file)) {
646: try {
647: removeFromCvsIgnore(file);
648: } catch (IOException e) {
649: ErrorManager.getDefault().notify(e);
650: }
651: } else {
652: unignoreOverride.add(file);
653: fileStatusCache.refresh(file,
654: FileStatusCache.REPOSITORY_STATUS_UNKNOWN);
655: }
656: }
657: }
658:
659: private void addToCvsIgnore(File file) throws IOException {
660:
661: Set<String> entries = readCvsIgnoreEntries(file.getParentFile());
662: String patternToIgnore = computePatternToIgnore(file.getName());
663: if (entries.add(patternToIgnore)) {
664: writeCvsIgnoreEntries(file.getParentFile(), entries);
665: }
666: }
667:
668: private String computePatternToIgnore(String name) {
669: return name.replace(' ', '?');
670: }
671:
672: private void removeFromCvsIgnore(File file) throws IOException {
673: Set entries = readCvsIgnoreEntries(file.getParentFile());
674: String patternToIgnore = computePatternToIgnore(file.getName());
675: if (entries.remove(patternToIgnore)) {
676: writeCvsIgnoreEntries(file.getParentFile(), entries);
677: }
678: }
679:
680: private Set<String> readCvsIgnoreEntries(File directory)
681: throws IOException {
682: File cvsIgnore = new File(directory, FILENAME_CVSIGNORE);
683:
684: Set<String> entries = new HashSet<String>(5);
685: if (!cvsIgnore.canRead())
686: return entries;
687:
688: String s;
689: BufferedReader r = null;
690: try {
691: r = new BufferedReader(new FileReader(cvsIgnore));
692: while ((s = r.readLine()) != null) {
693: entries.addAll(Arrays.asList(s.trim().split(" ")));
694: }
695: } finally {
696: if (r != null)
697: try {
698: r.close();
699: } catch (IOException e) {
700: }
701: }
702: return entries;
703: }
704:
705: private void writeCvsIgnoreEntries(File directory, Set entries)
706: throws IOException {
707: File cvsIgnore = new File(directory, FILENAME_CVSIGNORE);
708: FileObject fo = FileUtil.toFileObject(cvsIgnore);
709:
710: if (entries.size() == 0) {
711: if (fo != null)
712: fo.delete();
713: return;
714: }
715:
716: if (fo == null || !fo.isValid()) {
717: fo = FileUtil.toFileObject(directory);
718: fo = fo.createData(FILENAME_CVSIGNORE);
719: }
720: FileLock lock = fo.lock();
721: PrintWriter w = null;
722: try {
723: w = new PrintWriter(fo.getOutputStream(lock));
724: for (Iterator i = entries.iterator(); i.hasNext();) {
725: w.println(i.next());
726: }
727: } finally {
728: lock.releaseLock();
729: if (w != null)
730: w.close();
731: }
732: }
733:
734: /** @see FilesystemHandler#ignoreEvents */
735: public static void ignoreFilesystemEvents(boolean ignore) {
736: FilesystemHandler.ignoreEvents(ignore);
737: }
738:
739: /**
740: * Creates new GlobalOptions prefilled with default options:
741: * <ul>
742: * <li>compression level 3 if not enabled logging
743: * </ul>
744: */
745: public static GlobalOptions createGlobalOptions() {
746: GlobalOptions globalOptions = new GlobalOptions();
747: if (System.getProperty("cvsClientLog") == null) { // NOI18N
748: int gzipLevel = 4;
749: String level = System
750: .getProperty("netbeans.experimental.cvs.io.compressionLevel"); // NOI18N
751: if (level != null) {
752: try {
753: int candidate = Integer.parseInt(level);
754: if (0 <= candidate && candidate < 10) {
755: gzipLevel = candidate;
756: }
757: } catch (NumberFormatException ex) {
758: // default level
759: }
760: }
761: if (gzipLevel > 0) {
762: globalOptions.setCompressionLevel(gzipLevel);
763: }
764: }
765: return globalOptions;
766: }
767:
768: public VCSAnnotator getVCSAnnotator() {
769: return fileStatusProvider;
770: }
771:
772: public VCSInterceptor getVCSInterceptor() {
773: return filesystemHandler;
774: }
775:
776: private static final int STATUS_DIFFABLE = FileInformation.STATUS_VERSIONED_UPTODATE
777: | FileInformation.STATUS_VERSIONED_MODIFIEDLOCALLY
778: | FileInformation.STATUS_VERSIONED_MODIFIEDINREPOSITORY
779: | FileInformation.STATUS_VERSIONED_CONFLICT
780: | FileInformation.STATUS_VERSIONED_MERGE
781: | FileInformation.STATUS_VERSIONED_REMOVEDINREPOSITORY
782: | FileInformation.STATUS_VERSIONED_MODIFIEDINREPOSITORY
783: | FileInformation.STATUS_VERSIONED_MODIFIEDINREPOSITORY;
784:
785: public void getOriginalFile(File workingCopy, File originalFile) {
786: FileInformation info = fileStatusCache.getStatus(workingCopy);
787: if ((info.getStatus() & STATUS_DIFFABLE) == 0)
788: return;
789:
790: // TODO: it is not easy to tell whether the file is not yet versioned OR some real error occurred
791: try {
792: if (CvsVersioningSystem.getInstance().getAdminHandler()
793: .getEntry(workingCopy) == null) {
794: // VC would throw IAE in this case, so silently return here
795: // TODO: this can happen because of implicit logic in the cache that treats not-present files as uptodate, thus failing the STATUS_DIFFABLE test above
796: return;
797: }
798: File original = VersionsCache.getInstance().getRemoteFile(
799: workingCopy, VersionsCache.REVISION_BASE, null,
800: true);
801: if (original == null)
802: throw new IOException("Unable to get BASE revision of "
803: + workingCopy);
804: org.netbeans.modules.versioning.util.Utils
805: .copyStreamsCloseAll(new FileOutputStream(
806: originalFile),
807: new FileInputStream(original));
808: } catch (Exception e) {
809: Logger.getLogger(CvsVersioningSystem.class.getName()).log(
810: Level.INFO, "Unable to get original file", e);
811: }
812: }
813:
814: public void refreshAllAnnotations() {
815: listenerSupport.fireVersioningEvent(EVENT_REFRESH_ANNOTATIONS);
816: }
817: }
|