blob: 5c1dd9a48707ed77cc21cfe73a2187a83eb875a2 [file] [log] [blame]
/*
* 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.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.modules.mercurial.ui.branch.HgBranch;
import org.netbeans.modules.mercurial.ui.log.HgLogMessage;
import org.netbeans.modules.mercurial.util.HgCommand;
import org.openide.util.RequestProcessor;
/**
*
* @author ondra
*/
public class WorkingCopyInfo {
/**
* Fired when a working copy parents change
*/
public static final String PROPERTY_WORKING_COPY_PARENT = WorkingCopyInfo.class.getName() + ".workingCopyParents"; //NOI18N
/**
* Fired when the current branch name changes
*/
public static final String PROPERTY_CURRENT_BRANCH = WorkingCopyInfo.class.getName() + ".currBranch"; //NOI18N
private static final WeakHashMap<File, WorkingCopyInfo> cache = new WeakHashMap<File, WorkingCopyInfo>(5);
private static final Logger LOG = Logger.getLogger(WorkingCopyInfo.class.getName());
private static final RequestProcessor rp = new RequestProcessor("WorkingCopyInfo", 1, true); //NOI18N
private static final RequestProcessor.Task refreshTask = rp.create(new RepositoryRefreshTask());
private static final Set<WorkingCopyInfo> repositoriesToRefresh = new HashSet<WorkingCopyInfo>(2);
private final WeakReference<File> rootRef;
private final PropertyChangeSupport propertyChangeSupport;
private HgLogMessage[] parents = new HgLogMessage[0];
private String branch = HgBranch.DEFAULT_NAME;
private WorkingCopyInfo (File root) {
rootRef = new WeakReference<File>(root);
propertyChangeSupport = new PropertyChangeSupport(this);
}
/**
* Do NOT call from EDT
* @param repositoryRoot existing repository root
* @return null if repositoryRoot is not an existing git repository
*/
public static WorkingCopyInfo getInstance (File repositoryRoot) {
WorkingCopyInfo info = null;
// this should return alwaus the same instance, so the cache can be implemented as a weak map.
File repositoryRootSingleInstance = Mercurial.getInstance().getRepositoryRoot(repositoryRoot);
if (repositoryRoot.equals(repositoryRootSingleInstance)) {
synchronized (cache) {
info = cache.get(repositoryRootSingleInstance);
if (info == null) {
cache.put(repositoryRootSingleInstance, info = new WorkingCopyInfo(repositoryRootSingleInstance));
info.refresh();
}
}
}
return info;
}
public void addPropertyChangeListener (PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
/**
* Do NOT call from EDT
* @return
*/
public void refresh () {
assert !java.awt.EventQueue.isDispatchThread();
File root = rootRef.get();
try {
if (root == null) {
LOG.log(Level.WARNING, "refresh (): root is null, it has been collected in the meantime"); //NOI18N
} else {
LOG.log(Level.FINE, "refresh (): starting for {0}", root); //NOI18N
List<HgLogMessage> parentInfo = HgCommand.getParents(root, null, null);
setParents(parentInfo);
String branch = HgCommand.getBranch(root);
setBranch(branch);
}
} catch (HgException.HgCommandCanceledException ex) {
// nothing
} catch (HgException ex) {
Level level = root.exists() ? Level.INFO : Level.FINE; // do not polute the message log with messages concerning temporary or deleted repositories
LOG.log(level, null, ex);
}
}
public static void refreshAsync (File repositoryRoot) {
WorkingCopyInfo info = null;
synchronized (cache) {
info = cache.get(repositoryRoot);
}
if (info != null) {
boolean start = false;
synchronized (repositoriesToRefresh) {
start = repositoriesToRefresh.add(info);
}
if (start) {
LOG.log(Level.FINE, "Planning refresh for {0}", repositoryRoot); //NOI18N
refreshTask.schedule(3000);
}
}
}
public HgLogMessage[] getWorkingCopyParents () {
return parents;
}
public String getCurrentBranch () {
return branch;
}
private void setParents (List<HgLogMessage> parents) {
HgLogMessage[] oldParents = this.parents;
boolean changed = oldParents.length != parents.size();
if (!changed) {
for (HgLogMessage newParent : parents) {
boolean contains = false;
for (HgLogMessage oldParent : oldParents) {
if (oldParent.getCSetShortID().equals(newParent.getCSetShortID())
&& oldParent.getTags().length == newParent.getTags().length
&& new HashSet<String>(Arrays.asList(oldParent.getTags())).equals(new HashSet<String>(Arrays.asList(newParent.getTags())))) {
contains = true;
break;
}
}
if (!contains) {
changed = true;
break;
}
}
}
if (changed) {
HgLogMessage[] newParents = parents.toArray(new HgLogMessage[parents.size()]);
this.parents = newParents;
propertyChangeSupport.firePropertyChange(PROPERTY_WORKING_COPY_PARENT, oldParents, newParents);
}
}
private void setBranch (String branch) {
if (branch == null) {
branch = HgBranch.DEFAULT_NAME;
}
String oldBranch = this.branch;
this.branch = branch;
if (!oldBranch.equals(this.branch)) {
propertyChangeSupport.firePropertyChange(PROPERTY_CURRENT_BRANCH, oldBranch, branch);
}
}
private static class RepositoryRefreshTask implements Runnable {
@Override
public void run() {
WorkingCopyInfo info;
while ((info = getNextRepositoryInfo()) != null) {
info.refresh();
}
}
private WorkingCopyInfo getNextRepositoryInfo () {
WorkingCopyInfo info = null;
synchronized (repositoriesToRefresh) {
Iterator<WorkingCopyInfo> it = repositoriesToRefresh.iterator();
if (it.hasNext()) {
info = it.next();
it.remove();
}
}
return info;
}
}
}