Source Code Cross Referenced for FileStatusCache.java in  » IDE-Netbeans » subversion » org » netbeans » modules » subversion » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » subversion » org.netbeans.modules.subversion 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:
0042:        package org.netbeans.modules.subversion;
0043:
0044:        import java.io.File;
0045:        import java.util.regex.*;
0046:        import org.netbeans.modules.versioning.util.ListenersSupport;
0047:        import org.netbeans.modules.versioning.util.VersioningListener;
0048:        import org.netbeans.modules.versioning.spi.VersioningSupport;
0049:        import org.netbeans.modules.subversion.util.Context;
0050:        import org.netbeans.modules.subversion.util.SvnUtils;
0051:        import org.netbeans.modules.turbo.Turbo;
0052:        import org.netbeans.modules.turbo.CustomProviders;
0053:        import org.openide.filesystems.FileUtil;
0054:        import org.openide.filesystems.FileObject;
0055:        import org.openide.filesystems.FileStateInvalidException;
0056:        import java.util.*;
0057:        import org.netbeans.modules.subversion.client.SvnClient;
0058:        import org.netbeans.modules.subversion.client.SvnClientExceptionHandler;
0059:        import org.netbeans.modules.versioning.util.Utils;
0060:        import org.openide.filesystems.FileSystem;
0061:        import org.openide.util.RequestProcessor;
0062:        import org.tigris.subversion.svnclientadapter.*;
0063:        import org.tigris.subversion.svnclientadapter.ISVNStatus;
0064:
0065:        /**
0066:         * Central part of Subversion status management, deduces and caches statuses of files under version control.
0067:         * 
0068:         * @author Maros Sandor
0069:         */
0070:        public class FileStatusCache implements  ISVNNotifyListener {
0071:
0072:            /**
0073:             * Indicates that status of a file changed and listeners SHOULD check new status 
0074:             * values if they are interested in this file.
0075:             * First parameter: File whose status changes
0076:             * Second parameter: old FileInformation object, may be null
0077:             * Third parameter: new FileInformation object
0078:             */
0079:            public static final Object EVENT_FILE_STATUS_CHANGED = new Object();
0080:
0081:            /**
0082:             * A special map saying that no file inside the folder is managed.
0083:             */
0084:            private static final Map<File, FileInformation> NOT_MANAGED_MAP = new NotManagedMap();
0085:
0086:            public static final ISVNStatus REPOSITORY_STATUS_UNKNOWN = null;
0087:
0088:            // Constant FileInformation objects that can be safely reused
0089:            // Files that have a revision number cannot share FileInformation objects 
0090:            private static final FileInformation FILE_INFORMATION_EXCLUDED = new FileInformation(
0091:                    FileInformation.STATUS_NOTVERSIONED_EXCLUDED, false);
0092:            private static final FileInformation FILE_INFORMATION_EXCLUDED_DIRECTORY = new FileInformation(
0093:                    FileInformation.STATUS_NOTVERSIONED_EXCLUDED, true);
0094:            private static final FileInformation FILE_INFORMATION_UPTODATE_DIRECTORY = new FileInformation(
0095:                    FileInformation.STATUS_VERSIONED_UPTODATE, true);
0096:            private static final FileInformation FILE_INFORMATION_NOTMANAGED = new FileInformation(
0097:                    FileInformation.STATUS_NOTVERSIONED_NOTMANAGED, false);
0098:            private static final FileInformation FILE_INFORMATION_NOTMANAGED_DIRECTORY = new FileInformation(
0099:                    FileInformation.STATUS_NOTVERSIONED_NOTMANAGED, true);
0100:            private static final FileInformation FILE_INFORMATION_UNKNOWN = new FileInformation(
0101:                    FileInformation.STATUS_UNKNOWN, false);
0102:
0103:            /**
0104:             * Auxiliary conflict file siblings
0105:             * After update: *.r#, *.mine
0106:             * After merge: *.working, *.merge-right.r#, *.metge-left.r#
0107:             */
0108:            private static final Pattern auxConflictPattern = Pattern
0109:                    .compile("(.*?)\\.((r\\d+)|(mine)|" + // NOI18N
0110:                            "(working)|(merge-right\\.r\\d+)|((merge-left.r\\d+)))$"); // NOI18N
0111:
0112:            /*
0113:             * Holds three kinds of information: what folders we have scanned, what files we have found
0114:             * and what statuses of these files are.
0115:             * If a directory is not found as a key in the map, we have not scanned it yet.
0116:             * If it has been scanned, it maps to a Set of files that were found somehow out of sync with the
0117:             * repository (have any other status then up-to-date). In case all files are up-to-date, it maps
0118:             * to Collections.EMPTY_MAP. Entries in this map are created as directories are scanne, are never removed and
0119:             * are updated by the refresh method.
0120:             */
0121:
0122:            private final Turbo turbo;
0123:
0124:            /**
0125:             * Identifies attribute that holds information about all non STATUS_VERSIONED_UPTODATE files.
0126:             *
0127:             * <p>Key type: File identifying a folder
0128:             * <p>Value type: Map&lt;File, FileInformation>
0129:             */
0130:            private final String FILE_STATUS_MAP = DiskMapTurboProvider.ATTR_STATUS_MAP;
0131:
0132:            private DiskMapTurboProvider cacheProvider;
0133:
0134:            private Subversion svn;
0135:
0136:            private Set<FileSystem> filesystemsToRefresh;
0137:
0138:            private RequestProcessor.Task refreshFilesystemsTask;
0139:
0140:            FileStatusCache() {
0141:                this .svn = Subversion.getInstance();
0142:                cacheProvider = new DiskMapTurboProvider();
0143:                turbo = Turbo.createCustom(new CustomProviders() {
0144:                    private final Set providers = Collections
0145:                            .singleton(cacheProvider);
0146:
0147:                    public Iterator providers() {
0148:                        return providers.iterator();
0149:                    }
0150:                }, 200, 5000);
0151:            }
0152:
0153:            // --- Public interface -------------------------------------------------
0154:
0155:            /**
0156:             * Lists <b>modified files</b> and all folders that are known to be inside
0157:             * this folder. There are locally modified files present
0158:             * plus any files that exist in the folder in the remote repository. It
0159:             * returns all folders, including CVS folders.
0160:             *    
0161:             * @param dir folder to list
0162:             * @return
0163:             */
0164:            public File[] listFiles(File dir) {
0165:                Set<File> files = getScannedFiles(dir).keySet();
0166:                return files.toArray(new File[files.size()]);
0167:            }
0168:
0169:            /**
0170:             * Lists <b>interesting files</b> that are known to be inside given folders.
0171:             * These are locally and remotely modified and ignored files.
0172:             *
0173:             * <p>Comapring to CVS this method returns both folders and files.
0174:             *
0175:             * @param context context to examine
0176:             * @param includeStatus limit returned files to those having one of supplied statuses
0177:             * @return File [] array of interesting files
0178:             */
0179:            public File[] listFiles(Context context, int includeStatus) {
0180:                Set<File> set = new HashSet<File>();
0181:                Map allFiles = cacheProvider.getAllModifiedValues();
0182:                for (Iterator i = allFiles.keySet().iterator(); i.hasNext();) {
0183:                    File file = (File) i.next();
0184:                    FileInformation info = (FileInformation) allFiles.get(file);
0185:                    if ((info.getStatus() & includeStatus) == 0)
0186:                        continue;
0187:                    File[] roots = context.getRootFiles();
0188:                    for (int j = 0; j < roots.length; j++) {
0189:                        File root = roots[j];
0190:                        if (VersioningSupport.isFlat(root)) {
0191:                            if (file.equals(root)
0192:                                    || file.getParentFile().equals(root)) {
0193:                                set.add(file);
0194:                                break;
0195:                            }
0196:                        } else {
0197:                            if (SvnUtils.isParentOrEqual(root, file)) {
0198:                                set.add(file);
0199:                                break;
0200:                            }
0201:                        }
0202:                    }
0203:                }
0204:                if (context.getExclusions().size() > 0) {
0205:                    for (Iterator i = context.getExclusions().iterator(); i
0206:                            .hasNext();) {
0207:                        File excluded = (File) i.next();
0208:                        for (Iterator j = set.iterator(); j.hasNext();) {
0209:                            File file = (File) j.next();
0210:                            if (SvnUtils.isParentOrEqual(excluded, file)) {
0211:                                j.remove();
0212:                            }
0213:                        }
0214:                    }
0215:                }
0216:                return set.toArray(new File[set.size()]);
0217:            }
0218:
0219:            /**
0220:             * Lists <b>interesting files</b> that are known to be inside given folders.
0221:             * These are locally and remotely modified and ignored files.
0222:             *
0223:             * <p>Comapring to CVS this method returns both folders and files.
0224:             *
0225:             * @param roots context to examine
0226:             * @param includeStatus limit returned files to those having one of supplied statuses
0227:             * @return File [] array of interesting files
0228:             */
0229:            public File[] listFiles(File[] roots, int includeStatus) {
0230:                Set<File> set = new HashSet<File>();
0231:                Map allFiles = cacheProvider.getAllModifiedValues();
0232:                for (Iterator i = allFiles.keySet().iterator(); i.hasNext();) {
0233:                    File file = (File) i.next();
0234:                    FileInformation info = (FileInformation) allFiles.get(file);
0235:                    if ((info.getStatus() & includeStatus) == 0)
0236:                        continue;
0237:                    for (int j = 0; j < roots.length; j++) {
0238:                        File root = roots[j];
0239:                        if (VersioningSupport.isFlat(root)) {
0240:                            if (file.getParentFile().equals(root)) {
0241:                                set.add(file);
0242:                                break;
0243:                            }
0244:                        } else {
0245:                            if (SvnUtils.isParentOrEqual(root, file)) {
0246:                                set.add(file);
0247:                                break;
0248:                            }
0249:                        }
0250:                    }
0251:                }
0252:                return set.toArray(new File[set.size()]);
0253:            }
0254:
0255:            /**
0256:             * Determines the versioning status of a file. This method accesses disk and may block for a long period of time.
0257:             * 
0258:             * @param file file to get status for
0259:             * @return FileInformation structure containing the file status
0260:             * @see FileInformation
0261:             */
0262:            public FileInformation getStatus(File file) {
0263:                if (svn.isAdministrative(file))
0264:                    return FILE_INFORMATION_NOTMANAGED_DIRECTORY;
0265:                File dir = file.getParentFile();
0266:                if (dir == null) {
0267:                    return FILE_INFORMATION_NOTMANAGED; //default for filesystem roots 
0268:                }
0269:                Map files = getScannedFiles(dir);
0270:                if (files == NOT_MANAGED_MAP)
0271:                    return FILE_INFORMATION_NOTMANAGED;
0272:                FileInformation fi = (FileInformation) files.get(file);
0273:                if (fi != null) {
0274:                    return fi;
0275:                }
0276:                if (!exists(file))
0277:                    return FILE_INFORMATION_UNKNOWN;
0278:                if (file.isDirectory()) {
0279:                    return refresh(file, REPOSITORY_STATUS_UNKNOWN);
0280:                } else {
0281:                    return new FileInformation(
0282:                            FileInformation.STATUS_VERSIONED_UPTODATE, false);
0283:                }
0284:            }
0285:
0286:            /**
0287:             * Looks up cacnehed file status.
0288:             * 
0289:             * @param file file to check
0290:             * @return give file's status or null if the file's status is not in cache
0291:             */
0292:            FileInformation getCachedStatus(File file) {
0293:                File parent = file.getParentFile();
0294:                if (parent == null)
0295:                    return FILE_INFORMATION_NOTMANAGED_DIRECTORY;
0296:                Map<File, FileInformation> files = (Map<File, FileInformation>) turbo
0297:                        .readEntry(parent, FILE_STATUS_MAP);
0298:                return files != null ? files.get(file) : null;
0299:            }
0300:
0301:            private FileInformation refresh(File file,
0302:                    ISVNStatus repositoryStatus, boolean forceChangeEvent) {
0303:
0304:                boolean refreshDone = false;
0305:                FileInformation current = null;
0306:                FileInformation fi = null;
0307:                File[] content = null;
0308:
0309:                synchronized (this ) {
0310:                    File dir = file.getParentFile();
0311:                    if (dir == null) {
0312:                        return FILE_INFORMATION_NOTMANAGED; //default for filesystem roots 
0313:                    }
0314:                    Map<File, FileInformation> files = getScannedFiles(dir);
0315:                    if (files == NOT_MANAGED_MAP
0316:                            && repositoryStatus == REPOSITORY_STATUS_UNKNOWN)
0317:                        return FILE_INFORMATION_NOTMANAGED;
0318:                    current = files.get(file);
0319:
0320:                    ISVNStatus status = null;
0321:                    try {
0322:                        SvnClient client = Subversion.getInstance().getClient(
0323:                                false);
0324:                        status = client.getSingleStatus(file);
0325:                        if (status != null
0326:                                && SVNStatusKind.UNVERSIONED.equals(status
0327:                                        .getTextStatus())) {
0328:                            status = null;
0329:                        }
0330:                    } catch (SVNClientException e) {
0331:                        // svnClientAdapter does not return SVNStatusKind.UNVERSIONED!!!
0332:                        // unversioned resource is expected getSingleStatus()
0333:                        // does not return SVNStatusKind.UNVERSIONED but throws exception instead            
0334:                        // instead of throwing exception
0335:                        if (SvnClientExceptionHandler.isUnversionedResource(e
0336:                                .getMessage()) == false) {
0337:                            // missing or damaged entries
0338:                            // or ignored file
0339:                            SvnClientExceptionHandler.notifyException(e, false,
0340:                                    false);
0341:                        }
0342:                    }
0343:                    fi = createFileInformation(file, status, repositoryStatus);
0344:                    if (equivalent(fi, current)) {
0345:                        refreshDone = true;
0346:                    }
0347:                    // do not include uptodate files into cache, missing directories must be included
0348:                    if (!refreshDone
0349:                            && current == null
0350:                            && !fi.isDirectory()
0351:                            && fi.getStatus() == FileInformation.STATUS_VERSIONED_UPTODATE) {
0352:                        refreshDone = true;
0353:                    }
0354:
0355:                    if (!refreshDone) {
0356:                        if (fi.getStatus() == FileInformation.STATUS_UNKNOWN
0357:                                && current != null
0358:                                && current.isDirectory()
0359:                                && (current.getStatus() == FileInformation.STATUS_VERSIONED_DELETEDLOCALLY || current
0360:                                        .getStatus() == FileInformation.STATUS_VERSIONED_REMOVEDLOCALLY)) {
0361:                            // - if the file was deleted then all it's children have to be refreshed.
0362:                            // - we have to list the children before the turbo.writeEntry() call 
0363:                            //   as that unfortunatelly tends to purge them from the cache 
0364:                            content = listFiles(new File[] { file }, ~0);
0365:                        }
0366:
0367:                        file = FileUtil.normalizeFile(file);
0368:                        dir = FileUtil.normalizeFile(dir);
0369:                        Map<File, FileInformation> newFiles = new HashMap<File, FileInformation>(
0370:                                files);
0371:                        if (fi.getStatus() == FileInformation.STATUS_UNKNOWN) {
0372:                            newFiles.remove(file);
0373:                            turbo.writeEntry(file, FILE_STATUS_MAP, null); // remove mapping in case of directories
0374:                        } else if (fi.getStatus() == FileInformation.STATUS_VERSIONED_UPTODATE
0375:                                && file.isFile()) {
0376:                            newFiles.remove(file);
0377:                        } else {
0378:                            newFiles.put(file, fi);
0379:                        }
0380:                        assert newFiles.containsKey(dir) == false;
0381:                        turbo.writeEntry(dir, FILE_STATUS_MAP,
0382:                                newFiles.size() == 0 ? null : newFiles);
0383:                    }
0384:                }
0385:
0386:                if (!refreshDone) {
0387:                    if (content == null && file.isDirectory()
0388:                            && needRecursiveRefresh(fi, current)) {
0389:                        content = listFiles(file);
0390:                    }
0391:
0392:                    if (content != null) {
0393:                        for (int i = 0; i < content.length; i++) {
0394:                            refresh(content[i], REPOSITORY_STATUS_UNKNOWN);
0395:                        }
0396:                    }
0397:                    fireFileStatusChanged(file, current, fi);
0398:                } else if (forceChangeEvent) {
0399:                    fireFileStatusChanged(file, current, fi);
0400:                }
0401:                return fi;
0402:            }
0403:
0404:            /**
0405:             * Refreshes the status of the file given the repository status. Repository status is filled
0406:             * in when this method is called while processing server output. 
0407:             *
0408:             * <p>Note: it's not necessary if you use Subversion.getClient(), it
0409:             * updates the cache automatically using onNotify(). It's not
0410:             * fully reliable for removed files.
0411:             *
0412:             * @param file
0413:             * @param repositoryStatus
0414:             */
0415:            public FileInformation refresh(File file,
0416:                    ISVNStatus repositoryStatus) {
0417:                return refresh(file, repositoryStatus, false);
0418:            }
0419:
0420:            /**
0421:             * Two FileInformation objects are equivalent if their status contants are equal AND they both reperesent a file (or
0422:             * both represent a directory) AND Entries they cache, if they can be compared, are equal. 
0423:             *  
0424:             * @param other object to compare to
0425:             * @return true if status constants of both object are equal, false otherwise
0426:             */
0427:            private static boolean equivalent(FileInformation main,
0428:                    FileInformation other) {
0429:                if (other == null || main.getStatus() != other.getStatus()
0430:                        || main.isDirectory() != other.isDirectory())
0431:                    return false;
0432:
0433:                ISVNStatus e1 = main.getEntry(null);
0434:                ISVNStatus e2 = other.getEntry(null);
0435:                return e1 == e2 || e1 == null || e2 == null || equal(e1, e2);
0436:            }
0437:
0438:            /**
0439:             * Replacement for missing Entry.equals(). It is implemented as a separate method to maintain compatibility.
0440:             * 
0441:             * @param e1 first entry to compare
0442:             * @param e2 second Entry to compare
0443:             * @return true if supplied entries contain equivalent information
0444:             */
0445:            private static boolean equal(ISVNStatus e1, ISVNStatus e2) {
0446:                long r1 = -1;
0447:                if (e1 != null) {
0448:                    SVNRevision r = e1.getRevision();
0449:                    r1 = r != null ? e1.getRevision().getNumber() : r1;
0450:                }
0451:
0452:                long r2 = -2;
0453:                if (e2 != null) {
0454:                    SVNRevision r = e1.getRevision();
0455:                    r2 = r != null ? e2.getRevision().getNumber() : r2;
0456:                }
0457:
0458:                if (r1 != r2) {
0459:                    return false;
0460:                }
0461:                return e1.getUrl() == e2.getUrl() || e1.getUrl() != null
0462:                        && e1.getUrl().equals(e2.getUrl());
0463:            }
0464:
0465:            private boolean needRecursiveRefresh(FileInformation fi,
0466:                    FileInformation current) {
0467:                // XXX review this part and see also the places where svnutils.refreshrecursively is called.
0468:                //     looks like the same thing is done at diferent places in a different way but the same result.
0469:                if (fi.getStatus() == FileInformation.STATUS_NOTVERSIONED_EXCLUDED
0470:                        || current != null
0471:                        && current.getStatus() == FileInformation.STATUS_NOTVERSIONED_EXCLUDED)
0472:                    return true;
0473:                if (fi.getStatus() == FileInformation.STATUS_NOTVERSIONED_NOTMANAGED
0474:                        || current != null
0475:                        && current.getStatus() == FileInformation.STATUS_NOTVERSIONED_NOTMANAGED)
0476:                    return true;
0477:                if (fi.getStatus() == FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY
0478:                        || current != null
0479:                        && current.getStatus() == FileInformation.STATUS_VERSIONED_ADDEDLOCALLY)
0480:                    return true;
0481:                return false;
0482:            }
0483:
0484:            /**
0485:             * Refreshes information about a given file or directory ONLY if its status is already cached. The
0486:             * only exception are non-existing files (new-in-repository) whose statuses are cached in all cases. 
0487:             *  
0488:             * @param file
0489:             * @param repositoryStatus
0490:             */
0491:            public void refreshCached(File file, ISVNStatus repositoryStatus) {
0492:                refresh(file, repositoryStatus);
0493:            }
0494:
0495:            /**
0496:             * Refreshes status of all files inside given context. Files that have some remote status, eg. REMOTELY_ADDED
0497:             * are brought back to UPTODATE.
0498:             * 
0499:             * @param ctx context to refresh
0500:             */
0501:            public void refreshCached(Context ctx) {
0502:
0503:                File[] files = listFiles(ctx, ~0);
0504:
0505:                for (int i = 0; i < files.length; i++) {
0506:                    File file = files[i];
0507:                    refreshCached(file, REPOSITORY_STATUS_UNKNOWN);
0508:                }
0509:            }
0510:
0511:            // --- Package private contract ------------------------------------------
0512:
0513:            Map<File, FileInformation> getAllModifiedFiles() {
0514:                return cacheProvider.getAllModifiedValues();
0515:            }
0516:
0517:            /**
0518:             * Refreshes given directory and all subdirectories.
0519:             *
0520:             * @param dir directory to refresh
0521:             */
0522:            void directoryContentChanged(File dir) {
0523:                Map originalFiles = (Map) turbo.readEntry(dir, FILE_STATUS_MAP);
0524:                if (originalFiles != null) {
0525:                    for (Iterator i = originalFiles.keySet().iterator(); i
0526:                            .hasNext();) {
0527:                        File file = (File) i.next();
0528:                        refresh(file, REPOSITORY_STATUS_UNKNOWN);
0529:                    }
0530:                }
0531:            }
0532:
0533:            /**
0534:             * Cleans up the cache by removing or correcting entries that are no longer valid or correct.
0535:             */
0536:            void cleanUp() {
0537:                Map files = cacheProvider.getAllModifiedValues();
0538:                for (Iterator i = files.keySet().iterator(); i.hasNext();) {
0539:                    File file = (File) i.next();
0540:                    FileInformation info = (FileInformation) files.get(file);
0541:                    if ((info.getStatus() & FileInformation.STATUS_LOCAL_CHANGE) != 0) {
0542:                        refresh(file, REPOSITORY_STATUS_UNKNOWN);
0543:                    } else if (info.getStatus() == FileInformation.STATUS_NOTVERSIONED_EXCLUDED) {
0544:                        // remove entries that were excluded but no longer exist
0545:                        // cannot simply call refresh on excluded files because of 'excluded on server' status
0546:                        if (!exists(file)) {
0547:                            refresh(file, REPOSITORY_STATUS_UNKNOWN);
0548:                        }
0549:                    }
0550:                }
0551:            }
0552:
0553:            // --- Private methods ---------------------------------------------------
0554:
0555:            private Map<File, FileInformation> getScannedFiles(File dir) {
0556:                Map<File, FileInformation> files;
0557:
0558:                // there are 2nd level nested admin dirs (.svn/tmp, .svn/prop-base, ...)
0559:
0560:                if (svn.isAdministrative(dir)) {
0561:                    return NOT_MANAGED_MAP;
0562:                }
0563:                File parent = dir.getParentFile();
0564:                if (parent != null && svn.isAdministrative(parent)) {
0565:                    return NOT_MANAGED_MAP;
0566:                }
0567:
0568:                files = (Map<File, FileInformation>) turbo.readEntry(dir,
0569:                        FILE_STATUS_MAP);
0570:                if (files != null)
0571:                    return files;
0572:                if (isNotManagedByDefault(dir)) {
0573:                    return NOT_MANAGED_MAP;
0574:                }
0575:
0576:                // scan and populate cache with results
0577:
0578:                dir = FileUtil.normalizeFile(dir);
0579:                files = scanFolder(dir); // must not execute while holding the lock, it may take long to execute
0580:                assert files.containsKey(dir) == false;
0581:                turbo.writeEntry(dir, FILE_STATUS_MAP, files);
0582:                for (Iterator i = files.keySet().iterator(); i.hasNext();) {
0583:                    File file = (File) i.next();
0584:                    FileInformation info = files.get(file);
0585:                    if ((info.getStatus() & FileInformation.STATUS_LOCAL_CHANGE) != 0) {
0586:                        fireFileStatusChanged(file, null, info);
0587:                    }
0588:                }
0589:                return files;
0590:            }
0591:
0592:            private boolean isNotManagedByDefault(File dir) {
0593:                return !dir.exists();
0594:            }
0595:
0596:            /**
0597:             * Scans all files in the given folder, computes and stores their CVS status. 
0598:             * 
0599:             * @param dir directory to scan
0600:             * @return Map map to be included in the status cache (File => FileInformation)
0601:             */
0602:            private Map<File, FileInformation> scanFolder(File dir) {
0603:                File[] files = dir.listFiles();
0604:                if (files == null)
0605:                    files = new File[0];
0606:                Map<File, FileInformation> folderFiles = new HashMap<File, FileInformation>(
0607:                        files.length);
0608:
0609:                ISVNStatus[] entries = null;
0610:                try {
0611:                    if (Subversion.getInstance().isManaged(dir)) {
0612:                        SvnClient client = Subversion.getInstance().getClient(
0613:                                true);
0614:                        entries = client.getStatus(dir, false, false);
0615:                    }
0616:                } catch (SVNClientException e) {
0617:                    // no or damaged entries
0618:                    //LOG.getDefault().annotate(e, "Can not status " + dir.getAbsolutePath() + ", guessing it...");  // NOI18N
0619:                    SvnClientExceptionHandler.notifyException(e, false, false);
0620:                }
0621:
0622:                if (entries == null) {
0623:                    for (int i = 0; i < files.length; i++) {
0624:                        File file = files[i];
0625:                        if (svn.isAdministrative(file))
0626:                            continue;
0627:                        FileInformation fi = createFileInformation(file, null,
0628:                                REPOSITORY_STATUS_UNKNOWN);
0629:                        if (fi.isDirectory()
0630:                                || fi.getStatus() != FileInformation.STATUS_VERSIONED_UPTODATE) {
0631:                            folderFiles.put(file, fi);
0632:                        }
0633:                    }
0634:                } else {
0635:                    Set<File> localFiles = new HashSet<File>(Arrays
0636:                            .asList(files));
0637:                    for (int i = 0; i < entries.length; i++) {
0638:                        ISVNStatus entry = entries[i];
0639:                        File file = new File(entry.getPath());
0640:                        if (file.equals(dir)) {
0641:                            continue;
0642:                        }
0643:                        localFiles.remove(file);
0644:                        if (svn.isAdministrative(file)) {
0645:                            continue;
0646:                        }
0647:                        FileInformation fi = createFileInformation(file, entry,
0648:                                REPOSITORY_STATUS_UNKNOWN);
0649:                        if (fi.isDirectory()
0650:                                || fi.getStatus() != FileInformation.STATUS_VERSIONED_UPTODATE) {
0651:                            folderFiles.put(file, fi);
0652:                        }
0653:                    }
0654:
0655:                    Iterator it = localFiles.iterator();
0656:                    while (it.hasNext()) {
0657:                        File localFile = (File) it.next();
0658:                        FileInformation fi = createFileInformation(localFile,
0659:                                null, REPOSITORY_STATUS_UNKNOWN);
0660:                        if (fi.isDirectory()
0661:                                || fi.getStatus() != FileInformation.STATUS_VERSIONED_UPTODATE) {
0662:                            folderFiles.put(localFile, fi);
0663:                        }
0664:                    }
0665:                }
0666:
0667:                return folderFiles;
0668:            }
0669:
0670:            /**
0671:             * Examines a file or folder and computes its status. 
0672:             * 
0673:             * @param status entry for this file or null if the file is unknown to subversion
0674:             * @return FileInformation file/folder status bean
0675:             */
0676:            private FileInformation createFileInformation(File file,
0677:                    ISVNStatus status, ISVNStatus repositoryStatus) {
0678:                if (status == null
0679:                        || status.getTextStatus().equals(
0680:                                SVNStatusKind.UNVERSIONED)) {
0681:                    if (!svn.isManaged(file)) {
0682:                        return file.isDirectory() ? FILE_INFORMATION_NOTMANAGED_DIRECTORY
0683:                                : FILE_INFORMATION_NOTMANAGED;
0684:                    }
0685:                    return createMissingEntryFileInformation(file,
0686:                            repositoryStatus);
0687:                } else {
0688:                    return createVersionedFileInformation(file, status,
0689:                            repositoryStatus);
0690:                }
0691:            }
0692:
0693:            /**
0694:             * Examines a file or folder that has an associated CVS entry. 
0695:             * 
0696:             * @param file file/folder to examine
0697:             * @param status status of the file/folder as reported by the CVS server 
0698:             * @return FileInformation file/folder status bean
0699:             */
0700:            private FileInformation createVersionedFileInformation(File file,
0701:                    ISVNStatus status, ISVNStatus repositoryStatus) {
0702:
0703:                SVNStatusKind kind = status.getTextStatus();
0704:                SVNStatusKind pkind = status.getPropStatus();
0705:
0706:                int remoteStatus = 0;
0707:                if (repositoryStatus != REPOSITORY_STATUS_UNKNOWN) {
0708:                    if (repositoryStatus.getRepositoryTextStatus() == SVNStatusKind.MODIFIED
0709:                            || repositoryStatus.getRepositoryPropStatus() == SVNStatusKind.MODIFIED) {
0710:                        remoteStatus = FileInformation.STATUS_VERSIONED_MODIFIEDINREPOSITORY;
0711:                    } else if (repositoryStatus.getRepositoryTextStatus() == SVNStatusKind.DELETED
0712:                    /*|| repositoryStatus.getRepositoryPropStatus() == SVNStatusKind.DELETED*/) {
0713:                        remoteStatus = FileInformation.STATUS_VERSIONED_REMOVEDINREPOSITORY;
0714:                    } else if (repositoryStatus.getRepositoryTextStatus() == SVNStatusKind.ADDED
0715:                    /*|| repositoryStatus.getRepositoryPropStatus() == SVNStatusKind.ADDED*/) {
0716:                        // solved in createMissingfileInformation
0717:                    } else if (repositoryStatus.getRepositoryTextStatus() == null
0718:                            && repositoryStatus.getRepositoryPropStatus() == null) {
0719:                        // no remote change at all
0720:                    } else {
0721:                        // TODO systematically handle all repository statuses
0722:                        // so far above were observed....
0723:                        // XXX
0724:                        System.err
0725:                                .println("SVN.FSC: unhandled repository status: "
0726:                                        + file.getAbsolutePath()); // NOI18N
0727:                        System.err.println("\ttext: "
0728:                                + repositoryStatus.getRepositoryTextStatus()); // NOI18N
0729:                        System.err.println("\tprop: "
0730:                                + repositoryStatus.getRepositoryPropStatus()); // NOI18N
0731:                    }
0732:                }
0733:
0734:                if (status.getLockOwner() != null) {
0735:                    remoteStatus = FileInformation.STATUS_LOCKED | remoteStatus;
0736:                }
0737:
0738:                if (SVNStatusKind.NONE.equals(pkind)) {
0739:                    // no influence
0740:                } else if (SVNStatusKind.NORMAL.equals(pkind)) {
0741:                    // no influence
0742:                } else if (SVNStatusKind.MODIFIED.equals(pkind)) {
0743:                    if (SVNStatusKind.NORMAL.equals(kind)) {
0744:                        return new FileInformation(
0745:                                FileInformation.STATUS_VERSIONED_MODIFIEDLOCALLY
0746:                                        | remoteStatus, status);
0747:                    }
0748:                } else if (SVNStatusKind.CONFLICTED.equals(pkind)) {
0749:                    return new FileInformation(
0750:                            FileInformation.STATUS_VERSIONED_CONFLICT
0751:                                    | remoteStatus, status);
0752:                } else {
0753:                    throw new IllegalArgumentException("Unknown prop status: "
0754:                            + status.getPropStatus()); // NOI18N
0755:                }
0756:
0757:                if (SVNStatusKind.NONE.equals(kind)) {
0758:                    return FILE_INFORMATION_UNKNOWN;
0759:                } else if (SVNStatusKind.NORMAL.equals(kind)) {
0760:                    return new FileInformation(
0761:                            FileInformation.STATUS_VERSIONED_UPTODATE
0762:                                    | remoteStatus, status);
0763:                } else if (SVNStatusKind.MODIFIED.equals(kind)) {
0764:                    return new FileInformation(
0765:                            FileInformation.STATUS_VERSIONED_MODIFIEDLOCALLY
0766:                                    | remoteStatus, status);
0767:                } else if (SVNStatusKind.ADDED.equals(kind)) {
0768:                    return new FileInformation(
0769:                            FileInformation.STATUS_VERSIONED_ADDEDLOCALLY
0770:                                    | remoteStatus, status);
0771:                } else if (SVNStatusKind.DELETED.equals(kind)) {
0772:                    return new FileInformation(
0773:                            FileInformation.STATUS_VERSIONED_REMOVEDLOCALLY
0774:                                    | remoteStatus, status);
0775:                } else if (SVNStatusKind.UNVERSIONED.equals(kind)) {
0776:                    return new FileInformation(
0777:                            FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY
0778:                                    | remoteStatus, status);
0779:                } else if (SVNStatusKind.MISSING.equals(kind)) {
0780:                    return new FileInformation(
0781:                            FileInformation.STATUS_VERSIONED_DELETEDLOCALLY
0782:                                    | remoteStatus, status);
0783:                } else if (SVNStatusKind.REPLACED.equals(kind)) {
0784:                    // XXX  create new status constant? Is it neccesary to visualize
0785:                    // this status or better to use this simplyfication?
0786:                    return new FileInformation(
0787:                            FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY
0788:                                    | remoteStatus, status);
0789:                } else if (SVNStatusKind.MERGED.equals(kind)) {
0790:                    return new FileInformation(
0791:                            FileInformation.STATUS_VERSIONED_MERGE
0792:                                    | remoteStatus, status);
0793:                } else if (SVNStatusKind.CONFLICTED.equals(kind)) {
0794:                    return new FileInformation(
0795:                            FileInformation.STATUS_VERSIONED_CONFLICT
0796:                                    | remoteStatus, status);
0797:                } else if (SVNStatusKind.OBSTRUCTED.equals(kind)) {
0798:                    // TODO: create new status constant?
0799:                    return new FileInformation(
0800:                            FileInformation.STATUS_VERSIONED_CONFLICT
0801:                                    | remoteStatus, status);
0802:                } else if (SVNStatusKind.IGNORED.equals(kind)) {
0803:                    return new FileInformation(
0804:                            FileInformation.STATUS_NOTVERSIONED_EXCLUDED
0805:                                    | remoteStatus, status);
0806:                } else if (SVNStatusKind.INCOMPLETE.equals(kind)) {
0807:                    // TODO: create new status constant?
0808:                    return new FileInformation(
0809:                            FileInformation.STATUS_VERSIONED_CONFLICT
0810:                                    | remoteStatus, status);
0811:                } else if (SVNStatusKind.EXTERNAL.equals(kind)) {
0812:                    // TODO: create new status constant?
0813:                    return new FileInformation(
0814:                            FileInformation.STATUS_VERSIONED_UPTODATE
0815:                                    | remoteStatus, status);
0816:                } else {
0817:                    throw new IllegalArgumentException("Unknown text status: "
0818:                            + status.getTextStatus()); // NOI18N
0819:                }
0820:            }
0821:
0822:            static String statusText(ISVNStatus status) {
0823:                return "file: " + status.getTextStatus().toString()
0824:                        + " copied: " + status.isCopied() + " prop: "
0825:                        + status.getPropStatus().toString(); // NOI18N
0826:            }
0827:
0828:            /**
0829:             * Examines a file or folder that does NOT have an associated Subversion status. 
0830:             * 
0831:             * @param file file/folder to examine
0832:             * @return FileInformation file/folder status bean
0833:             */
0834:            private FileInformation createMissingEntryFileInformation(
0835:                    File file, ISVNStatus repositoryStatus) {
0836:
0837:                // ignored status applies to whole subtrees
0838:                boolean isDirectory = file.isDirectory();
0839:                int parentStatus = getStatus(file.getParentFile()).getStatus();
0840:                if (parentStatus == FileInformation.STATUS_NOTVERSIONED_EXCLUDED) {
0841:                    return isDirectory ? FILE_INFORMATION_EXCLUDED_DIRECTORY
0842:                            : FILE_INFORMATION_EXCLUDED;
0843:                }
0844:                if (parentStatus == FileInformation.STATUS_NOTVERSIONED_NOTMANAGED) {
0845:                    if (isDirectory) {
0846:                        // Working directory roots (aka managed roots). We already know that isManaged(file) is true
0847:                        return SvnUtils.isPartOfSubversionMetadata(file) ? FILE_INFORMATION_NOTMANAGED_DIRECTORY
0848:                                : FILE_INFORMATION_UPTODATE_DIRECTORY;
0849:                    } else {
0850:                        return FILE_INFORMATION_NOTMANAGED;
0851:                    }
0852:                }
0853:
0854:                // mark auxiliary after-update conflict files as ignored
0855:                // C source.java
0856:                // I source.java.mine
0857:                // I source.java.r45
0858:                // I source.java.r57
0859:                //
0860:                // XXX:svnClientAdapter design:  why is not it returned from getSingleStatus() ?
0861:                //
0862:                // after-merge conflicts (even svn st does not recognize as ignored)
0863:                // C source.java
0864:                // ? source.java.working
0865:                // ? source.jave.merge-right.r20
0866:                // ? source.java.merge-left.r0
0867:                //
0868:                // XXX:svn-cli design:  why is not it returned from getSingleStatus() ?
0869:
0870:                String name = file.getName();
0871:                Matcher m = auxConflictPattern.matcher(name);
0872:                if (m.matches()) {
0873:                    File dir = file.getParentFile();
0874:                    if (dir != null) {
0875:                        String masterName = m.group(1);
0876:                        File master = new File(dir, masterName);
0877:                        if (master.isFile()) {
0878:                            return FILE_INFORMATION_EXCLUDED;
0879:                        }
0880:                    }
0881:                }
0882:
0883:                if (file.exists()) {
0884:                    if (Subversion.getInstance().isIgnored(file)) {
0885:                        return new FileInformation(
0886:                                FileInformation.STATUS_NOTVERSIONED_EXCLUDED,
0887:                                file.isDirectory());
0888:                    } else {
0889:                        return new FileInformation(
0890:                                FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY,
0891:                                file.isDirectory());
0892:                    }
0893:                } else {
0894:                    if (repositoryStatus != REPOSITORY_STATUS_UNKNOWN) {
0895:                        if (repositoryStatus.getRepositoryTextStatus() == SVNStatusKind.ADDED) {
0896:                            boolean folder = repositoryStatus.getNodeKind() == SVNNodeKind.DIR;
0897:                            return new FileInformation(
0898:                                    FileInformation.STATUS_VERSIONED_NEWINREPOSITORY,
0899:                                    folder);
0900:                        }
0901:                    }
0902:                    return FILE_INFORMATION_UNKNOWN;
0903:                }
0904:            }
0905:
0906:            private boolean exists(File file) {
0907:                if (!file.exists())
0908:                    return false;
0909:                return file.getAbsolutePath().equals(
0910:                        FileUtil.normalizeFile(file).getAbsolutePath());
0911:            }
0912:
0913:            ListenersSupport listenerSupport = new ListenersSupport(this );
0914:
0915:            public void addVersioningListener(VersioningListener listener) {
0916:                listenerSupport.addListener(listener);
0917:            }
0918:
0919:            public void removeVersioningListener(VersioningListener listener) {
0920:                listenerSupport.removeListener(listener);
0921:            }
0922:
0923:            private void fireFileStatusChanged(File file,
0924:                    FileInformation oldInfo, FileInformation newInfo) {
0925:                listenerSupport.fireVersioningEvent(EVENT_FILE_STATUS_CHANGED,
0926:                        new Object[] { file, oldInfo, newInfo });
0927:            }
0928:
0929:            public void setCommand(int command) {
0930:                // boring ISVNNotifyListener event
0931:            }
0932:
0933:            public void logCommandLine(String commandLine) {
0934:                // boring ISVNNotifyListener event
0935:            }
0936:
0937:            public void logMessage(String message) {
0938:                // boring ISVNNotifyListener event
0939:            }
0940:
0941:            public void logError(String message) {
0942:                // boring ISVNNotifyListener event
0943:            }
0944:
0945:            public void logRevision(long revision, String path) {
0946:                // boring ISVNNotifyListener event
0947:            }
0948:
0949:            public void logCompleted(String message) {
0950:                // boring ISVNNotifyListener event
0951:            }
0952:
0953:            public void onNotify(final File path, final SVNNodeKind kind) {
0954:                // notifications from the svnclientadapter may be caused by a synchronously handled FS event. 
0955:                // The thing is that we have to prevent reentrant calls on the FS api ...
0956:                Utils.post(new Runnable() {
0957:                    public void run() {
0958:                        onNotifyImpl(path, kind);
0959:                    }
0960:                });
0961:            }
0962:
0963:            private void onNotifyImpl(File path, SVNNodeKind kind) {
0964:                if (path == null) { // on kill
0965:                    return;
0966:                }
0967:
0968:                // I saw "./"
0969:                path = FileUtil.normalizeFile(path);
0970:
0971:                // ISVNNotifyListener event
0972:                // invalidate cached status
0973:                // force event: an updated file changes status from uptodate to uptodate but its entry changes
0974:                refresh(path, REPOSITORY_STATUS_UNKNOWN, true);
0975:
0976:                // collect the filesystems to notify them in SvnClientInvocationHandler about the external change
0977:                for (;;) {
0978:                    FileObject fo = FileUtil.toFileObject(path);
0979:                    if (fo != null) {
0980:                        try {
0981:                            Set<FileSystem> filesystems = getFilesystemsToRefresh();
0982:                            synchronized (filesystems) {
0983:                                filesystems.add(fo.getFileSystem());
0984:                            }
0985:                        } catch (FileStateInvalidException e) {
0986:                            // ignore invalid filesystems
0987:                        }
0988:                        break;
0989:                    } else {
0990:                        path = path.getParentFile();
0991:                        if (path == null)
0992:                            break;
0993:                    }
0994:                }
0995:            }
0996:
0997:            public void refreshDirtyFileSystems() {
0998:                if (refreshFilesystemsTask == null) {
0999:                    RequestProcessor rp = new RequestProcessor();
1000:                    refreshFilesystemsTask = rp.create(new Runnable() {
1001:                        public void run() {
1002:                            Set<FileSystem> filesystems = getFilesystemsToRefresh();
1003:                            FileSystem[] filesystemsToRefresh = new FileSystem[filesystems
1004:                                    .size()];
1005:                            synchronized (filesystems) {
1006:                                filesystemsToRefresh = filesystems
1007:                                        .toArray(new FileSystem[filesystems
1008:                                                .size()]);
1009:                                filesystems.clear();
1010:                            }
1011:                            for (int i = 0; i < filesystemsToRefresh.length; i++) {
1012:                                // don't call refresh() in synchronized (filesystems). It may lead to a deadlock.
1013:                                filesystemsToRefresh[i].refresh(true);
1014:                            }
1015:                        }
1016:                    });
1017:                }
1018:                refreshFilesystemsTask.schedule(200);
1019:            }
1020:
1021:            private Set<FileSystem> getFilesystemsToRefresh() {
1022:                if (filesystemsToRefresh == null) {
1023:                    filesystemsToRefresh = new HashSet<FileSystem>();
1024:                }
1025:                return filesystemsToRefresh;
1026:            }
1027:
1028:            private static final class NotManagedMap extends
1029:                    AbstractMap<File, FileInformation> {
1030:                public Set<Entry<File, FileInformation>> entrySet() {
1031:                    return Collections.emptySet();
1032:                }
1033:            }
1034:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.