Package org.nbgit

Source Code of org.nbgit.StatusCache$ChangedEvent

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2008 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License.  When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun
* Microsystems, Inc. All Rights Reserved.
* Portions Copyright 2008 Alexander Coles (Ikonoklastik Productions).
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.nbgit;

import java.io.IOException;
import org.nbgit.util.GitUtils;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.nbgit.util.GitCommand;
import org.nbgit.util.exclude.Excludes;
import org.netbeans.modules.turbo.CustomProviders;
import org.netbeans.modules.turbo.Turbo;
import org.netbeans.modules.versioning.spi.VCSContext;
import org.netbeans.modules.versioning.spi.VersioningSupport;
import org.netbeans.modules.versioning.util.Utils;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileUtil;

/**
* Central part of status management, deduces and caches statuses of files under version control.
*
* @author Maros Sandor
*/
public class StatusCache {

    /**
     * Indicates that status of a file changed and listeners SHOULD check new status
     * values if they are interested in this file.
     * The New value is a ChangedEvent object (old StatusInfo object may be null)
     */
    public static final String PROP_FILE_STATUS_CHANGED = "status.changed"; // NOI18N
    /**
     * A special map saying that no file inside the folder is managed.
     */
    private static final Map<File, StatusInfo> NOT_MANAGED_MAP = new NotManagedMap();
    public static final File REPOSITORY_STATUS_UNKNOWN = null// Constant File objects that can be safely reused
    // Files that have a revision number cannot share StatusInfo objects
    private static final StatusInfo FILE_STATUS_EXCLUDED = new StatusInfo(StatusInfo.STATUS_NOTVERSIONED_EXCLUDED, false);
    private static final StatusInfo FILE_STATUS_EXCLUDED_DIRECTORY = new StatusInfo(StatusInfo.STATUS_NOTVERSIONED_EXCLUDED, true);
    private static final StatusInfo FILE_STATUS_UPTODATE_DIRECTORY = new StatusInfo(StatusInfo.STATUS_VERSIONED_UPTODATE, true);
    private static final StatusInfo FILE_STATUS_NOTMANAGED = new StatusInfo(StatusInfo.STATUS_NOTVERSIONED_NOTMANAGED, false);
    private static final StatusInfo FILE_STATUS_NOTMANAGED_DIRECTORY = new StatusInfo(StatusInfo.STATUS_NOTVERSIONED_NOTMANAGED, true);
    private static final StatusInfo FILE_STATUS_UNKNOWN = new StatusInfo(StatusInfo.STATUS_UNKNOWN, false);
    private static final StatusInfo FILE_STATUS_NEWLOCALLY = new StatusInfo(StatusInfo.STATUS_NOTVERSIONED_NEWLOCALLY, false);
    private static final StatusInfo FILE_STATUS_CONFLICT = new StatusInfo(StatusInfo.STATUS_VERSIONED_CONFLICT, false);
    private static final StatusInfo FILE_STATUS_REMOVEDLOCALLY = new StatusInfo(StatusInfo.STATUS_VERSIONED_REMOVEDLOCALLY, false);
    private PropertyChangeSupport listenerSupport = new PropertyChangeSupport(this);
    /**
     * Caches status of files in memory and on disk
     */
    private final Turbo turbo;
    private final String FILE_STATUS_MAP = DiskMapTurboProvider.ATTR_STATUS_MAP;
    private DiskMapTurboProvider cacheProvider;
    private Git git;
    private Set<FileSystem> filesystemsToRefresh;

    StatusCache(Git git) {
        this.git = git;
        cacheProvider = new DiskMapTurboProvider();
        turbo = Turbo.createCustom(new CustomProviders() {

            private final Set providers = Collections.singleton(cacheProvider);

            public Iterator providers() {
                return providers.iterator();
            }
        }, 200, 5000);
    }

    // --- Public interface -------------------------------------------------
    /**
     * Lists <b>modified files</b> and all folders that are known to be inside
     * this folder. There are locally modified files present
     * plus any files that exist in the folder in the remote repository.
     *
     * @param dir folder to list
     * @return
     */
    public File[] listFiles(File dir) {
        Set<File> files = getScannedFiles(dir, null).keySet();
        return files.toArray(new File[files.size()]);
    }

    /**
     * Lists <b>interesting files</b> that are known to be inside given folders.
     * These are locally and remotely modified and ignored files.
     *
     * <p>This method returns both folders and files.
     *
     * @param context context to examine
     * @param includeStatus limit returned files to those having one of supplied statuses
     * @return File [] array of interesting files
     */
    public File[] listFiles(VCSContext context, int includeStatus) {
        Set<File> set = new HashSet<File>();
        Map<File, StatusInfo> allFiles = cacheProvider.getAllModifiedValues();
        if (allFiles == null) {
            Git.LOG.log(Level.FINE, "StatusCache: listFiles(): allFiles == null"); // NOI18N
            return new File[0];
        }

        for (File file : allFiles.keySet()) {
            StatusInfo info = allFiles.get(file);
            if ((info.getStatus() & includeStatus) == 0) {
                continue;
            }
            Set<File> roots = context.getRootFiles();
            for (File root : roots) {
                if (VersioningSupport.isFlat(root)) {
                    if (file.equals(root) || file.getParentFile().equals(root)) {
                        set.add(file);
                        break;
                    }
                } else if (Utils.isAncestorOrEqual(root, file)) {
                    set.add(file);
                    break;
                }
            }
        }
        if (context.getExclusions().size() > 0) {
            for (File excluded : context.getExclusions()) {
                for (Iterator<File> j = set.iterator(); j.hasNext();) {
                    if (Utils.isAncestorOrEqual(excluded, j.next())) {
                        j.remove();
                    }
                }
            }
        }
        return set.toArray(new File[set.size()]);
    }

    /**
     * Lists <b>interesting files</b> that are known to be inside given folders.
     * These are locally and remotely modified and ignored files.
     *
     * <p>Comparing to CVS this method returns both folders and files.
     *
     * @param roots context to examine
     * @param includeStatus limit returned files to those having one of supplied statuses
     * @return File [] array of interesting files
     */
    public File[] listFiles(File[] roots, int includeStatus) {
        Set<File> set = new HashSet<File>();
        Map<File, StatusInfo> allFiles = cacheProvider.getAllModifiedValues();

        for (File file : allFiles.keySet()) {
            StatusInfo info = allFiles.get(file);
            if ((info.getStatus() & includeStatus) == 0) {
                continue;
            }
            for (int j = 0; j < roots.length; j++) {
                File root = roots[j];
                if (VersioningSupport.isFlat(root)) {
                    if (file.getParentFile().equals(root)) {
                        set.add(file);
                        break;
                    }
                } else if (Utils.isAncestorOrEqual(root, file)) {
                    set.add(file);
                    break;
                }
            }
        }
        return set.toArray(new File[set.size()]);
    }

    /**
     * Check if this context has at least one file with the passed in status
     *
     * @param context context to examine
     * @param includeStatus file status to check for
     * @return boolean true if this context contains at least one file with the includeStatus, false otherwise
     */
    public boolean containsFileOfStatus(VCSContext context, int includeStatus) {
        Map<File, StatusInfo> allFiles = cacheProvider.getAllModifiedValues();
        if (allFiles == null) {
            Git.LOG.log(Level.FINE, "containsFileOfStatus(): allFiles == null"); // NOI18N
            return false;
        }

        Set<File> roots = context.getRootFiles();
        Set<File> exclusions = context.getExclusions();
        Set<File> setAllFiles = allFiles.keySet();
        boolean bExclusions = exclusions != null && exclusions.size() > 0;
        boolean bContainsFile = false;

        for (File file : setAllFiles) {
            StatusInfo info = allFiles.get(file);
            if ((info.getStatus() & includeStatus) == 0) {
                continue;
            }
            for (File root : roots) {
                if (VersioningSupport.isFlat(root)) {
                    if (file.equals(root) || file.getParentFile().equals(root)) {
                        bContainsFile = true;
                        break;
                    }
                } else if (Utils.isAncestorOrEqual(root, file)) {
                    bContainsFile = true;
                    break;
                }
            }
            // Check it is not an excluded file
            if (bContainsFile && bExclusions) {
                for (File excluded : exclusions) {
                    if (!Utils.isAncestorOrEqual(excluded, file)) {
                        return true;
                    }
                }
            } else if (bContainsFile) {
                return true;
            }
        }
        return false;
    }

    /**
     * Determines the versioning status of a file. This method accesses disk and may block for a long period of time.
     *
     * @param file file to get status for
     * @return StatusInfo structure containing the file status
     * @see StatusInfo
     */
    public StatusInfo getStatus(File file) {
        if (file.isDirectory() && excludeFile(file)) {
            return StatusCache.FILE_STATUS_EXCLUDED_DIRECTORY;
        }
        File dir = file.getParentFile();
        if (dir == null) {
            return StatusCache.FILE_STATUS_NOTMANAGED;
        }
        Map files = getScannedFiles(dir, null);
        if (files == StatusCache.NOT_MANAGED_MAP) {
            return StatusCache.FILE_STATUS_NOTMANAGED;
        }
        StatusInfo fi = (StatusInfo) files.get(file);
        if (fi != null) {
            return fi;
        }
        if (!exists(file)) {
            return StatusCache.FILE_STATUS_UNKNOWN;
        }
        if (file.isDirectory()) {
            return refresh(file, REPOSITORY_STATUS_UNKNOWN);
        } else {
            return new StatusInfo(StatusInfo.STATUS_VERSIONED_UPTODATE, false);
        }
    }

    /**
     * Looks up cached file status.
     *
     * @param file file to check
     * @return give file's status or null if the file's status is not in cache
     */
    @SuppressWarnings("unchecked") // Need to change turbo module to remove warning at source
    StatusInfo getCachedStatus( File file, boolean bCheckSharability) {
        File parent = file.getParentFile();
        if (parent == null) {
            return StatusCache.FILE_STATUS_NOTMANAGED_DIRECTORY;
        }
        Map<File, StatusInfo> files = (Map<File, StatusInfo>) turbo.readEntry(parent, FILE_STATUS_MAP);
        StatusInfo fi = files != null ? files.get(file) : null;
        if (fi != null) {
            return fi;
        }
        if (file.isDirectory()) {
            if (excludeFile(file)) {
                return StatusCache.FILE_STATUS_EXCLUDED_DIRECTORY;
            } else {
                return StatusCache.FILE_STATUS_UPTODATE_DIRECTORY;
            }
        }
        return fi;
    }

    private StatusInfo refresh(File file, File repoStat, boolean forceChangeEvent) {
        Git.LOG.log(Level.FINE, "refresh(): {0}", file); // NOI18N
        File dir = file.getParentFile();
        if (dir == null) {
            return StatusCache.FILE_STATUS_NOTMANAGED;
        }
        Map<File, StatusInfo> files = getScannedFiles(dir, null); // Has side effect of updating the cache
        if (files == StatusCache.NOT_MANAGED_MAP && repoStat == StatusCache.REPOSITORY_STATUS_UNKNOWN) {
            return StatusCache.FILE_STATUS_NOTMANAGED;
        }
        StatusInfo current = files.get(file);

        StatusInfo fi = createFileInformation(file, true);

        if (StatusInfo.equivalent(fi, current)) {
            if (forceChangeEvent) {
                fireFileStatusChanged(file, current, fi);
            }
            return fi;
        }

        // do not include uptodate files into cache, missing directories must be included
        if (current == null && !fi.isDirectory() && fi.getStatus() == StatusInfo.STATUS_VERSIONED_UPTODATE) {
            if (forceChangeEvent) {
                fireFileStatusChanged(file, current, fi);
            }
            return fi;
        }

        file = FileUtil.normalizeFile(file);
        dir = FileUtil.normalizeFile(dir);
        Map<File, StatusInfo> newFiles = new HashMap<File, StatusInfo>(files);
        if (fi.getStatus() == StatusInfo.STATUS_UNKNOWN) {
            newFiles.remove(file);
            turbo.writeEntry(file, FILE_STATUS_MAP, null)// remove mapping in case of directories
        } else if (fi.getStatus() == StatusInfo.STATUS_VERSIONED_UPTODATE && file.isFile()) {
            newFiles.remove(file);
        } else {
            newFiles.put(file, fi);
        }
        assert newFiles.containsKey(dir) == false;
        turbo.writeEntry(dir, FILE_STATUS_MAP, newFiles.size() == 0 ? null : newFiles);

        if (file.isDirectory() && needRecursiveRefresh(fi, current)) {
            File[] content = listFiles(file); // Has side effect of updating the cache
            for (int i = 0; i < content.length; i++) {
                refresh(content[i], StatusCache.REPOSITORY_STATUS_UNKNOWN);
            }
        }
        fireFileStatusChanged(file, current, fi);
        return fi;
    }

    private StatusInfo createFileInformation(File file, Boolean callStatus) {
        Git.LOG.log(Level.FINE, "createFileInformation(): {0} {1}", new Object[]{file, callStatus}); // NOI18N
        if (file == null) {
            return FILE_STATUS_UNKNOWN;
        }
        if (git.isAdministrative(file)) {
            return FILE_STATUS_EXCLUDED_DIRECTORY; // Excluded
        }
        File rootManagedFolder = git.getTopmostManagedParent(file);
        if (rootManagedFolder == null) {
            return FILE_STATUS_UNKNOWN; // Avoiding returning NOT_MANAGED dir or file
        }
        if (file.isDirectory()) {
            if (Excludes.isIgnored(file)) {
                return FILE_STATUS_EXCLUDED_DIRECTORY;
            } else {
                return FILE_STATUS_UPTODATE_DIRECTORY;
            }
        }
        if (callStatus == false) {
            if (Excludes.isIgnored(file)) {
                return FILE_STATUS_EXCLUDED;
            }
            return null;
        }

        return GitCommand.getSingleStatus(rootManagedFolder, file);
    }

    /**
     * Refreshes the status of the file given the repository status. Repository status is filled
     * in when this method is called while processing server output.
     *
     * <p>Note: it's not necessary if you use Subversion.getClient(), it
     * updates the cache automatically using onNotify(). It's not
     * fully reliable for removed files.
     *
     * @param file
     * @param repositoryStatus
     */
    public StatusInfo refresh(File file, File repositoryStatus) {
        return refresh(file, repositoryStatus, false);
    }

    @SuppressWarnings("unchecked") // Need to change turbo module to remove warning at source
    public Map<File, StatusInfo> getScannedFiles(File dir, Map<File, StatusInfo> interestingFiles) {
        Map<File, StatusInfo> files;

        files = (Map<File, StatusInfo>) turbo.readEntry(dir, FILE_STATUS_MAP);
        if (files != null) {
            return files;
        }
        if (!dir.exists() && interestingFiles == null) {
            return StatusCache.NOT_MANAGED_MAP;
        }
        dir = FileUtil.normalizeFile(dir);
        files = scanFolder(dir, interestingFiles);
        assert files.containsKey(dir) == false;
        turbo.writeEntry(dir, FILE_STATUS_MAP, files);
        if (interestingFiles == null) {
            for (File file : files.keySet()) {
                StatusInfo info = files.get(file);
                if ((info.getStatus() & (StatusInfo.STATUS_LOCAL_CHANGE | StatusInfo.STATUS_NOTVERSIONED_EXCLUDED)) != 0) {
                    fireFileStatusChanged(file, null, info);
                }
            }
        }
        return files;
    }

    public void refreshFileStatus(File file, StatusInfo fi, Map<File, StatusInfo> interestingFiles) {
        refreshFileStatus(file, fi, interestingFiles, false);
    }

    public void refreshFileStatus(File file, StatusInfo fi, Map<File, StatusInfo> interestingFiles, boolean alwaysFireEvent) {
        if (file == null || fi == null) {
            return;
        }
        File dir = file.getParentFile();
        if (dir == null) {
            return;
        }
        Map<File, StatusInfo> files = getScannedFiles(dir, interestingFiles);

        if (files == null || files == StatusCache.NOT_MANAGED_MAP) {
            return;
        }
        StatusInfo current = files.get(file);
        if (StatusInfo.equivalent(fi, current)) {
            if (StatusInfo.equivalent(FILE_STATUS_NEWLOCALLY, fi)) {
                if (Excludes.isIgnored(file)) {
                    Git.LOG.log(Level.FINE, "refreshFileStatus() file: {0} was LocallyNew but is NotSharable", file.getAbsolutePath()); // NOI18N
                    fi = FILE_STATUS_EXCLUDED;
                } else {
                    return;
                }
            } else if (!StatusInfo.equivalent(FILE_STATUS_REMOVEDLOCALLY, fi)) {
                return;
            }
        }
        if (StatusInfo.equivalent(FILE_STATUS_NEWLOCALLY, fi)) {
            if (StatusInfo.equivalent(FILE_STATUS_EXCLUDED, current)) {
                Git.LOG.log(Level.FINE, "refreshFileStatus() file: {0} was LocallyNew but is Excluded", file.getAbsolutePath()); // NOI18N
                return;
            } else if (current == null) {
                if (Excludes.isIgnored(file)) {
                    Git.LOG.log(Level.FINE, "refreshFileStatus() file: {0} was LocallyNew but current is null and is not NotSharable", file.getAbsolutePath()); // NOI18N
                    fi = FILE_STATUS_EXCLUDED;
                }
            }
        }
        file = FileUtil.normalizeFile(file);
        dir = FileUtil.normalizeFile(dir);
        Map<File, StatusInfo> newFiles = new HashMap<File, StatusInfo>(files);
        if (fi.getStatus() == StatusInfo.STATUS_UNKNOWN) {
            newFiles.remove(file);
            turbo.writeEntry(file, FILE_STATUS_MAP, null)// remove mapping in case of directories
        } else if (fi.getStatus() == StatusInfo.STATUS_VERSIONED_UPTODATE && file.isFile()) {
            newFiles.remove(file);
        } else {
            newFiles.put(file, fi);
        }
        assert files.containsKey(dir) == false;
        turbo.writeEntry(dir, FILE_STATUS_MAP, newFiles);

        if (interestingFiles == null) {
            fireFileStatusChanged(file, current, fi);
        } else if (alwaysFireEvent) {
            fireFileStatusChanged(file, null, fi);
        }
        return;
    }

    private boolean needRecursiveRefresh(StatusInfo fi, StatusInfo current) {
        if (fi.getStatus() == StatusInfo.STATUS_NOTVERSIONED_EXCLUDED ||
                current != null && current.getStatus() == StatusInfo.STATUS_NOTVERSIONED_EXCLUDED) {
            return true;
        }
        if (fi.getStatus() == StatusInfo.STATUS_NOTVERSIONED_NOTMANAGED ||
                current != null && current.getStatus() == StatusInfo.STATUS_NOTVERSIONED_NOTMANAGED) {
            return true;
        }
        return false;
    }

    /**
     * Refreshes information about a given file or directory ONLY if its status is already cached. The
     * only exception are non-existing files (new-in-repository) whose statuses are cached in all cases.
     *
     * @param file
     * @param repositoryStatus
     */
    public void refreshCached(File file, File repositoryStatus) {
        refresh(file, repositoryStatus);
    }

    /**
     * Refreshes status of the specfied file or all files inside the
     * specified directory.
     *
     * @param file
     */
    public void refreshCached(File root) {
        if (!root.isDirectory()) {
            refresh(root, StatusCache.REPOSITORY_STATUS_UNKNOWN);
            return;
        }

        File repository = git.getTopmostManagedParent(root);
        if (repository == null) {
            return;
        }
        File roots[] = new File[1];
        roots[0] = root;
        File[] files = listFiles(roots, ~0);
        if (files.length == 0) {
            return;
        }
        Map<File, StatusInfo> allFiles;
        try {
            allFiles = GitCommand.getAllStatus(repository, root);
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                StatusInfo fi = allFiles.get(file);
                if (fi == null) // We have a file in the cache which seems to have disappeared
                {
                    refresh(file, StatusCache.REPOSITORY_STATUS_UNKNOWN);
                } else {
                    refreshFileStatus(file, fi, null);
                }
            }
        } catch (IOException ex) {
            Git.LOG.log(Level.FINE, "refreshCached() file: {0} {1} { 2} ", new Object[]{repository.getAbsolutePath(), root.getAbsolutePath(), ex.toString()}); // NOI18N
        }
    }

    /**
     * Refreshes status of all files inside given context.
     *
     * @param ctx context to refresh
     */
    public void refreshCached(VCSContext ctx) {
        for (File root : ctx.getRootFiles()) {
            refreshCached(root);
        }
    }

    public void addToCache(Set<File> files) {
        StatusInfo fi = new StatusInfo(StatusInfo.STATUS_NOTVERSIONED_NEWLOCALLY, null, false);
        HashMap<File, Map<File, StatusInfo>> dirMap = new HashMap<File, Map<File, StatusInfo>>(files.size());

        for (File file : files) {
            File parent = file.getParentFile();
            file = FileUtil.normalizeFile(file);
            parent = FileUtil.normalizeFile(parent);
            Map<File, StatusInfo> currentDirMap = dirMap.get(parent);
            if (currentDirMap == null) {
                // 20 is a guess at number of files in a directory
                currentDirMap = new HashMap<File, StatusInfo>(20);
                dirMap.put(parent, currentDirMap);
            }
            currentDirMap.put(file, fi);
        }
        for (File dir : dirMap.keySet()) {
            dir = FileUtil.normalizeFile(dir);
            Map<File, StatusInfo> currentDirMap = dirMap.get(dir);
            turbo.writeEntry(dir, FILE_STATUS_MAP, currentDirMap);
        }
    }
    // --- Package private contract ------------------------------------------
    Map<File, StatusInfo> getAllModifiedFiles() {
        return cacheProvider.getAllModifiedValues();
    }

    /**
     * Refreshes given directory and all subdirectories.
     *
     * @param dir directory to refresh
     */
    void directoryContentChanged(File dir) {
        Map originalFiles = (Map) turbo.readEntry(dir, FILE_STATUS_MAP);
        if (originalFiles != null) {
            for (Iterator i = originalFiles.keySet().iterator(); i.hasNext();) {
                File file = (File) i.next();
                refresh(file, StatusCache.REPOSITORY_STATUS_UNKNOWN);
            }
        }
    }

    // --- Private methods ---------------------------------------------------
    private boolean isNotManagedByDefault(File dir) {
        return !dir.exists();
    }

    /**
     * Scans all files in the given folder, computes and stores their CVS status.
     *
     * @param dir directory to scan
     * @return Map map to be included in the status cache (File => StatusInfo)
     */
    private Map<File, StatusInfo> scanFolder(File dir, Map<File, StatusInfo> interestingFiles) {
        File[] files = dir.listFiles();
        if (files == null) {
            if (interestingFiles == null) {
                files = new File[0];
            } else {
                files = interestingFiles.keySet().toArray(new File[interestingFiles.keySet().size()]);
            }
        }
        Map<File, StatusInfo> folderFiles = new HashMap<File, StatusInfo>(files.length);

        Git.LOG.log(Level.FINE, "scanFolder(): {0}", dir); // NOI18N
        if (git.isAdministrative(dir)) {
            folderFiles.put(dir, FILE_STATUS_EXCLUDED_DIRECTORY); // Excluded dir
            return folderFiles;
        }

        File rootManagedFolder = git.getTopmostManagedParent(dir);
        if (rootManagedFolder == null) {
            // Only interested in looking for Git managed dirs
            for (File file : files) {
                if (file.isDirectory() && git.getTopmostManagedParent(file) != null) {
                    if (excludeFile(file)) {
                        Git.LOG.log(Level.FINE, "scanFolder NotMng Ignored Dir {0}: exclude SubDir: {1}", // NOI18N
                                new Object[]{dir.getAbsolutePath(), file.getName()});
                        folderFiles.put(file, FILE_STATUS_EXCLUDED_DIRECTORY); // Excluded dir
                    } else {
                        Git.LOG.log(Level.FINE, "scanFolder NotMng Dir {0}: up to date Dir: {1}", // NOI18N
                                new Object[]{dir.getAbsolutePath(), file.getName()});
                        folderFiles.put(file, FILE_STATUS_UPTODATE_DIRECTORY);
                    }
                // Do NOT put any unmanaged dir's (FILE_STATUS_NOTMANAGED_DIRECTORY) or
                // files (FILE_STATUS_NOTMANAGED) into the folderFiles
                }
            }
            return folderFiles;
        }

        if (Excludes.isIgnored(dir)) {
            for (File file : files) {
                if (GitUtils.isPartOfGitMetadata(file)) {
                    continue;
                }
                if (file.isDirectory()) {
                    folderFiles.put(file, FILE_STATUS_EXCLUDED_DIRECTORY); // Excluded dir
                    Git.LOG.log(Level.FINE, "scanFolder Mng Ignored Dir {0}: exclude SubDir: {1}", // NOI18N
                            new Object[]{dir.getAbsolutePath(), file.getName()});
                } else {
                    Git.LOG.log(Level.FINE, "scanFolder Mng Ignored Dir {0}: exclude File: {1}", // NOI18N
                            new Object[]{dir.getAbsolutePath(), file.getName()});
                    folderFiles.put(file, FILE_STATUS_EXCLUDED);
                }
            }
            return folderFiles;
        }

        if (interestingFiles == null) {
            interestingFiles = GitCommand.getInterestingStatus(rootManagedFolder, dir);
        }
        if (interestingFiles == null) {
            return folderFiles;
        }
        for (File file : files) {
            if (GitUtils.isPartOfGitMetadata(file)) {
                continue;
            }
            if (file.isDirectory()) {
                if (excludeFile(file)) {
                    Git.LOG.log(Level.FINE, "scanFolder Mng Dir {0}: exclude Dir: {1}", // NOI18N
                            new Object[]{dir.getAbsolutePath(), file.getName()});
                    folderFiles.put(file, FILE_STATUS_EXCLUDED_DIRECTORY); // Excluded dir
                } else {
                    Git.LOG.log(Level.FINE, "scanFolder Mng Dir {0}: up to date Dir: {1}", // NOI18N
                            new Object[]{dir.getAbsolutePath(), file.getName()});
                    folderFiles.put(file, FILE_STATUS_UPTODATE_DIRECTORY);
                }
            } else {
                StatusInfo fi = interestingFiles.get(file);
                if (fi == null) // We have removed -i from GitCommand.getInterestingFiles
                // so we might have a file we should be ignoring
                {
                    fi = createFileInformation(file, false);
                }
                if (fi != null && fi.getStatus() != StatusInfo.STATUS_VERSIONED_UPTODATE) {
                    folderFiles.put(file, fi);
                }
            }
        }
        return folderFiles;
    }

    private boolean exists(File file) {
        if (!file.exists()) {
            return false;
        }
        return file.getAbsolutePath().equals(FileUtil.normalizeFile(file).getAbsolutePath());
    }

    public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
        listenerSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        listenerSupport.removePropertyChangeListener(listener);
    }

    private void fireFileStatusChanged(File file, StatusInfo oldInfo, StatusInfo newInfo) {
        listenerSupport.firePropertyChange(PROP_FILE_STATUS_CHANGED, null, new ChangedEvent(file, oldInfo, newInfo));
    }

    public void refreshDirtyFileSystems() {
        Set<FileSystem> filesystems = getFilesystemsToRefresh();
        FileSystem[] fstoRefresh = new FileSystem[filesystems.size()];
        synchronized (filesystems) {
            fstoRefresh = filesystems.toArray(new FileSystem[filesystems.size()]);
            filesystems.clear();
        }
        for (int i = 0; i < fstoRefresh.length; i++) {
            // don't call refresh() in synchronized (filesystems). It may lead to a deadlock.
            fstoRefresh[i].refresh(true);
        }
    }

    private Set<FileSystem> getFilesystemsToRefresh() {
        if (filesystemsToRefresh == null) {
            filesystemsToRefresh = new HashSet<FileSystem>();
        }
        return filesystemsToRefresh;
    }

    private boolean excludeFile(File file) {
        return git.isAdministrative(file) || Excludes.isIgnored(file);
    }

    private static final class NotManagedMap extends AbstractMap<File, StatusInfo> {

        public Set<Entry<File, StatusInfo>> entrySet() {
            return Collections.emptySet();
        }
    }

    public static class ChangedEvent {

        private File file;
        private StatusInfo oldInfo;
        private StatusInfo newInfo;

        public ChangedEvent(File file, StatusInfo oldInfo, StatusInfo newInfo) {
            this.file = file;
            this.oldInfo = oldInfo;
            this.newInfo = newInfo;
        }

        public File getFile() {
            return file;
        }

        public StatusInfo getOldInfo() {
            return oldInfo;
        }

        public StatusInfo getNewInfo() {
            return newInfo;
        }
    }
}
TOP

Related Classes of org.nbgit.StatusCache$ChangedEvent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
y>