All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.mercurial.HgHistoryProvider Maven / Gradle / Ivy

There is a newer version: RELEASE220
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.mercurial;

import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.SwingUtilities;
import org.netbeans.modules.mercurial.ui.log.HgLogMessage;
import org.netbeans.modules.mercurial.ui.log.HgLogMessage.HgRevision;
import org.netbeans.modules.mercurial.ui.log.LogAction;
import org.netbeans.modules.mercurial.ui.update.RevertModificationsAction;
import org.netbeans.modules.mercurial.util.HgUtils;
import org.netbeans.modules.versioning.history.HistoryAction;
import org.netbeans.modules.versioning.spi.VCSHistoryProvider;
import org.netbeans.modules.versioning.util.FileUtils;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;

/**
 *
 * @author Tomas Stupka
 */
public class HgHistoryProvider implements VCSHistoryProvider {
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private final List listeners = new LinkedList();
    private Action[] actions;

    private static final Logger LOG = Logger.getLogger(HgHistoryProvider.class.getName());
    
    @Override
    public void addHistoryChangeListener(VCSHistoryProvider.HistoryChangeListener l) {
        synchronized(listeners) {
            listeners.add(l);
        }
    }

    @Override
    public void removeHistoryChangeListener(VCSHistoryProvider.HistoryChangeListener l) {
        synchronized(listeners) {
            listeners.remove(l);
        }
    }
    
    @Override
    public synchronized HistoryEntry[] getHistory(File[] files, Date fromDate) {
        assert !SwingUtilities.isEventDispatchThread() : "Accessing remote repository. Do not call in awt!"; 
        
        logFiles("retrieving history for files: ", files); // NOi18N
        long t = System.currentTimeMillis();
        
        try {
            if(!isClientAvailable()) {
                LOG.log(Level.WARNING, "Mercurial client is unavailable");
                return null;
            }

            Set repositories = getRepositoryRoots(files);
            if(repositories == null) {
                return null;
            }

            List ret = new LinkedList();
            Map> rev2FileMap = new HashMap>();
            Map rev2LMMap = new HashMap();

            String fromRevision;
            String toRevision;
            if(fromDate == null) {
                fromRevision = "0";
                toRevision = "BASE";
            } else {
                fromRevision = dateFormat.format(fromDate);
                toRevision = dateFormat.format(new Date(System.currentTimeMillis()));
            }

            File repositoryRoot = repositories.iterator().next();
            for (File file : files) {
                FileInformation info = Mercurial.getInstance().getFileStatusCache().refresh(file);
                int status = info.getStatus();
                if ((status & FileInformation.STATUS_VERSIONED) == 0) {
                    continue;
                }
                HgLogMessage[] history = HistoryRegistry.getInstance().getLogs(repositoryRoot, files, fromRevision, toRevision);
                for (HgLogMessage h : history) {
                    String r = h.getHgRevision().getRevisionNumber();
                    rev2LMMap.put(r, h);
                    Set s = rev2FileMap.get(r);
                    if(s == null) {
                        s = new HashSet();
                        rev2FileMap.put(r, s);
                    }
                    s.add(file);
                }
            }    

            for(HgLogMessage h : rev2LMMap.values()) {
                Set s = rev2FileMap.get(h.getHgRevision().getRevisionNumber());
                File[] involvedFiles = s.toArray(new File[0]);
                ret.add(createHistoryEntry(h, repositoryRoot, involvedFiles));
            }
            return ret.toArray(new HistoryEntry[0]);
        } finally {
            LOG.log(Level.FINE, "retrieving history took {0}", (System.currentTimeMillis() - t));
        }
    }

    @Override
    public Action createShowHistoryAction(File[] files) {
        return new OpenHistoryAction(files);
    }
    
    public void fireHistoryChange(final File[] files) {
        final HistoryChangeListener[] la;
        synchronized(listeners) {
            la = listeners.toArray(new HistoryChangeListener[0]);
        }
        Mercurial.getInstance().getRequestProcessor().post(new Runnable() {
            @Override
            public void run() {
                for (HistoryChangeListener l : la) {
                    l.fireHistoryChanged(new HistoryEvent(HgHistoryProvider.this, files));
                }
            }
        });
    }

    private void logFiles(String msg, File[] files) {
        if(!LOG.isLoggable(Level.FINE)) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(msg);
        for (int i = 0; i < files.length; i++) {
            File f = files[i];
            if(f == null) {
                continue;
            }
            sb.append(f.getAbsolutePath());
            if(i < files.length - 1) {
                sb.append(","); // NOI18N
            }
        }
        LOG.fine(sb.toString());
    }

    private class RevisionProviderImpl implements RevisionProvider {
        private HgRevision hgRevision;

        public RevisionProviderImpl(HgRevision hgRevision) {
            this.hgRevision = hgRevision;
        }
        
        @Override
        public void getRevisionFile(File originalFile, File revisionFile) {
            assert !SwingUtilities.isEventDispatchThread() : "Accessing remote repository. Do not call in awt!";
            
            if(!isClientAvailable()) {
                LOG.log(Level.WARNING, "Mercurial client is unavailable");
                return;
            }

            try {
                FileInformation info = Mercurial.getInstance().getFileStatusCache().refresh(originalFile);
                if (info != null && (info.getStatus() & FileInformation.STATUS_VERSIONED_ADDEDLOCALLY) != 0
                        && info.getStatus(null) != null && info.getStatus(null).getOriginalFile() != null) 
                {
                    originalFile = info.getStatus(null).getOriginalFile();
                }
                
                Set repositories = getRepositoryRoots(originalFile);
                if(repositories == null || repositories.isEmpty()) {
                    LOG.log(Level.WARNING, "Repository root not found for file {0}", originalFile);
                    return;
                }
                File repository = repositories.iterator().next();
                File historyFile = HistoryRegistry.getInstance().getHistoryFile(repository, originalFile, hgRevision.getChangesetId(), true);
                if(historyFile != null) {
                    // ah! we already now the file was moved in the history,
                    // so lets look for contents by using its previous name
                    originalFile= historyFile;
                }
                File file = VersionsCache.getInstance().getFileRevision(originalFile, hgRevision, false);
                if(file != null) {
                    FileUtils.copyFile(file, revisionFile); // XXX lets be faster - LH should cache that somehow ...
                } else if(historyFile == null) {
                    // well then, lets try to find out if the file was move at some point in the history
                    LOG.log(Level.WARNING, "File {0} not found in revision {1}. Will make a guess ...", new Object[]{originalFile, hgRevision});
                    historyFile = HistoryRegistry.getInstance().getHistoryFile(repository, originalFile, hgRevision.getChangesetId(), false);
                    if(historyFile != null) {
                        file = VersionsCache.getInstance().getFileRevision(historyFile, hgRevision, false);
                        if(file != null) {
                            FileUtils.copyFile(file, revisionFile); // XXX lets be faster - LH should cache that somehow ...
                        }
                    }
                }
            } catch (IOException e) {
                if(e.getCause() instanceof HgException.HgCommandCanceledException)  {
                    LOG.log(Level.FINE, null, e);
                } else {
                    LOG.log(Level.WARNING, null, e);
                }
            }        
        }
    }

    private class ParentProviderImpl implements ParentProvider {
        private HgLogMessage logMessage;
        private File[] files;
        private File repository;

        public ParentProviderImpl(HgLogMessage logMessage, File[] files, File repository) {
            this.logMessage = logMessage;
            this.files = files;
            this.repository = repository;
        }

        @Override
        public HistoryEntry getParentEntry(File file) {
            HgRevision ancestor = logMessage.getAncestor(file);
            if (ancestor.equals(HgRevision.EMPTY)) {
                File originalFile = HistoryRegistry.getInstance().getHistoryFile(repository, file, logMessage.getCSetShortID(), false);
                if (originalFile != null) {
                    ancestor = logMessage.getAncestor(originalFile);
                }
            }
            if (ancestor.equals(HgRevision.EMPTY)) {
                return null;
            }
            HgLogMessage history = HistoryRegistry.getInstance().getLog(repository, file, ancestor.getChangesetId());
            if(history == null) {
                return null;
            }
            
            return createHistoryEntry(history, repository, files);
        }
    }
    
    private HistoryEntry createHistoryEntry(HgLogMessage h, File repository, File[] files) {
        String username = h.getUsername();
        String author = h.getAuthor();
        if(username == null || "".equals(username.trim())) { // NOI18N
            username = author;
        }
        return new HistoryEntry(
                files, 
                h.getDate(), 
                h.getMessage(), 
                author, 
                username, 
                h.getHgRevision().getRevisionNumber() + ":" + h.getHgRevision().getChangesetId(), // NOI18N
                h.getHgRevision().getRevisionNumber(), 
                getActions(), 
                new RevisionProviderImpl(h.getHgRevision()),
                null,
                new ParentProviderImpl(h, files, repository));
    }
    
    private static class OpenHistoryAction extends AbstractAction {
        private final File[] files;

        public OpenHistoryAction(File[] files) {
            this.files = files;
        }
        
        @Override
        public void actionPerformed(ActionEvent e) {
            openHistory(files);
        }
        private void openHistory(File[] files) {
            if(!isClientAvailable()) {
                LOG.log(Level.WARNING, "Mercurial client is unavailable"); // NOI18N
                return;
            }

            if(files == null || files.length == 0) {
                return;
            }
            Set repositories = getRepositoryRoots(files);
            if(repositories == null) {
                return;
            }
            LogAction.openHistory(repositories.iterator().next(), files);
        }
        
    }

    private synchronized Action[] getActions() {
        if(actions == null) {
            actions = new Action[] {
                new HistoryAction() {
                    @Override
                    protected void perform(final HistoryEntry entry, final Set files) {
                        final File root = Mercurial.getInstance().getRepositoryRoot(files.iterator().next());
                        RequestProcessor rp = Mercurial.getInstance().getRequestProcessor(root);
                        HgProgressSupport support = new HgProgressSupport() {
                            @Override
                            public void perform() {
                                RevertModificationsAction.performRevert(
                                    root,   
                                    getHgRevision(entry).getRevisionNumber(),                           
                                    new LinkedList(files), 
                                    HgModuleConfig.getDefault().getBackupOnRevertModifications(), 
                                    false, 
                                    this.getLogger());
                            }
                        };
                        support.start(rp, root, NbBundle.getMessage(LogAction.class, "MSG_Revert_Progress")); // NOI18N
                    }    
                    @Override
                    protected boolean isMultipleHistory() {
                        return false;
                    }
                    @Override
                    public String getName() {
                        String rev = getRevisionShort();
                        if(rev == null) {
                            rev = ""; // NOI18N
                        }
                        return NbBundle.getMessage(LogAction.class, "CTL_SummaryView_RollbackTo", rev);
                    }
                },
                new HistoryAction(NbBundle.getMessage(LogAction.class, "CTL_SummaryView_View")) { // NOI18N
                    @Override
                    protected void perform(HistoryEntry entry, Set files) {
                        view(entry, false, files);
                    }
                },
                new HistoryAction(NbBundle.getMessage(LogAction.class, "CTL_SummaryView_ShowAnnotations")) { // NOI18N
                    @Override
                    protected void perform(HistoryEntry entry, Set files) {
                        view(entry, true, files);
                    }
                }
            };
        }
        return actions;
    }
    
    private void view(final HistoryEntry entry, final boolean showAnnotations, final Set files) {
        final File root = Mercurial.getInstance().getRepositoryRoot(files.iterator().next());
        RequestProcessor rp = Mercurial.getInstance().getRequestProcessor(root);
        rp.post(new Runnable() {
            @Override
            public void run() {
                for (File f : files) {
                    try {
                        HgUtils.openInRevision(f, -1, getHgRevision(entry), showAnnotations);
                    } catch (IOException ex) {
                        // Ignore if file not available in cache
                    }
                }
            }
        });
    }
    
    private HgRevision getHgRevision(HistoryEntry entry) {
        String[] revs = entry.getRevision().split(":");
        final HgRevision revision = new HgRevision(revs[1], revs[0]);
        return revision;
    }
    
    /**
     * Returns true if mercurial client is installed and has a supported version.
* Does not show any warning dialog. * @return true if mercurial client is available. */ private static boolean isClientAvailable() { return isClientAvailable(false); } private static boolean isClientAvailable (boolean notifyUI) { return org.netbeans.modules.mercurial.Mercurial.getInstance().isAvailable(true, notifyUI); } private static Set getRepositoryRoots(File... files) { Set repositories = HgUtils.getRepositoryRoots(new HashSet(Arrays.asList(files))); if (repositories.size() != 1) { LOG.log(Level.WARNING, "History requested for {0} repositories", repositories.size()); // NOI18N return null; } return repositories; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy