blob: fd0cb3dc98f1e45a1288b283fbf1fa74febae6f6 [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.team.ide;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JFileChooser;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectInformation;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.ui.OpenProjects;
import org.netbeans.modules.team.ide.spi.IDEProject;
import org.netbeans.modules.team.ide.spi.ProjectServices;
import org.netbeans.spi.project.ui.support.CommonProjectActions;
import org.netbeans.spi.project.ui.support.ProjectChooser;
import org.openide.explorer.ExplorerManager;
import org.openide.filesystems.FileChangeAdapter;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
/**
*
* @author Tomas Stupka
*/
@org.openide.util.lookup.ServiceProvider(service=org.netbeans.modules.team.ide.spi.ProjectServices.class)
public class ProjectServicesImpl implements ProjectServices {
/** Listener on OpenProjects, bridging to the registered IDEProject.OpenListener listeners. */
private static ProjectOpenListener projectOpenListener;
/** Registered listeners from the UI of team project sources. Notified when
* new projects get opened. */
private static List<Reference<IDEProject.OpenListener>> ideProjectOpenListeners;
@Override
public FileObject[] getOpenProjectsDirectories() {
Project[] openProjects = OpenProjects.getDefault().getOpenProjects();
if (openProjects.length == 0) {
return null;
}
FileObject[] directories = new FileObject[openProjects.length];
for (int i = 0; i < openProjects.length; i++) {
Project project = openProjects[i];
directories[i] = project.getProjectDirectory();
}
return directories;
}
@Override
public FileObject getMainProjectDirectory() {
Project p = OpenProjects.getDefault().getMainProject();
return p != null ? p.getProjectDirectory() : null;
}
@Override
public FileObject getFileOwnerDirectory(FileObject fileObject) {
Project project = FileOwnerQuery.getOwner(fileObject);
return project != null ? project.getProjectDirectory() : null;
}
@Override
public FileObject[] getCurrentSelection() {
Node[] nodes = TopComponent.getRegistry().getActivatedNodes();
if(nodes == null) {
return null;
}
List<FileObject> ret = new ArrayList<FileObject>();
for(Node node : nodes) {
Lookup nodeLookup = node.getLookup();
Collection<? extends Project> projects = nodeLookup.lookupAll(Project.class);
if(projects != null && !projects.isEmpty()) {
for (Project project : projects) {
ret.add(project.getProjectDirectory());
}
} else {
DataObject dataObj = nodeLookup.lookup(DataObject.class);
if (dataObj != null) {
FileObject fileObj = dataObj.getPrimaryFile();
if (fileObj != null) {
ret.add(fileObj);
}
}
}
}
return ret.toArray(new FileObject[ret.size()]);
}
public FileObject[] getProjectDirectories(Lookup lookup) {
Collection<? extends Project> projects = lookup.lookupAll(Project.class);
if(projects == null) {
return null;
}
List<FileObject> ret = new ArrayList<FileObject>();
for (Project project : projects) {
ret.add(project.getProjectDirectory());
}
return ret.toArray(new FileObject[ret.size()]);
}
@Override
public <T> T runAfterProjectOpenFinished(Callable<T> operation) throws Exception {
// wait until projects are opened
OpenProjects.getDefault().openProjects().get();
return operation.call();
}
@Override
public boolean openProject(URL url) {
Project p = getProject(url);
if (p == null) {
return false;
}
OpenProjects.getDefault().open(new Project[] { p }, false);
TopComponent projectsTC = WindowManager.getDefault().findTopComponent("projectTabLogical_tc"); // NOI18N
projectsTC.requestActive();
ExplorerManager em = ((ExplorerManager.Provider) projectsTC).getExplorerManager();
Node root = em.getRootContext();
Node projNode = null;
for (Node n : root.getChildren().getNodes()) {
Project prj = n.getLookup().lookup(Project.class);
if (prj != null && prj.getProjectDirectory().equals(p.getProjectDirectory())) {
projNode = n;
break;
}
}
if (projNode == null) { // fallback
projNode = root.getChildren().findChild(ProjectUtils.getInformation(p).getName());
}
if (projNode != null) {
try {
em.setSelectedNodes(new Node[] { projNode });
} catch (Exception ignore) { // may ignore it
}
}
return true;
}
@Override
public void openOtherProject(File workingDir) {
chooseAndOpenProjects(workingDir, true);
}
@Override
public File[] chooseProjects(File workingDir) {
return chooseAndOpenProjects(workingDir, false);
}
private File[] chooseAndOpenProjects(File workingDir, boolean open) {
if (workingDir != null) {
ProjectChooser.setProjectsFolder(workingDir);
}
JFileChooser chooser = ProjectChooser.projectChooser();
if (workingDir != null) {
chooser.setCurrentDirectory(workingDir);
}
chooser.setMultiSelectionEnabled(true);
File[] projectDirs;
int option = chooser.showOpenDialog(WindowManager.getDefault().getMainWindow());
if (option == JFileChooser.APPROVE_OPTION) {
if (chooser.isMultiSelectionEnabled()) {
projectDirs = chooser.getSelectedFiles();
} else {
projectDirs = new File[] { chooser.getSelectedFile() };
}
if (open) {
ArrayList<Project> projects = new ArrayList<Project>(projectDirs.length);
for (File d : projectDirs) {
try {
Project p = ProjectManager.getDefault().findProject(FileUtil.toFileObject(d));
if (p != null) {
projects.add(p);
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
} catch (IllegalArgumentException ex) {
Exceptions.printStackTrace(ex);
}
}
if (!projects.isEmpty()) {
OpenProjects.getDefault().open(projects.toArray(new Project[projects.size()]), false);
}
WindowManager.getDefault().findTopComponent("projectTabLogical_tc").requestActive(); // NOI18N
}
} else {
projectDirs = new File[0];
}
return projectDirs;
}
@Override
public void reopenProjectsFromNewLocation(File[] oldLocations, File[] newLocations) {
List<Project> projectsToClose = new ArrayList<Project>();
List<Project> projectsToOpen = new ArrayList<Project>();
ProjectManager.getDefault().clearNonProjectCache();
for (int i=0; i < oldLocations.length; i++) {
Project prj = FileOwnerQuery.getOwner(FileUtil.toFileObject(oldLocations[i]));
if (prj != null) {
projectsToClose.add(prj);
}
}
for (int i=0; i < newLocations.length; i++) {
Project prj = FileOwnerQuery.getOwner(FileUtil.toFileObject(newLocations[i]));
if (prj != null) {
projectsToOpen.add(prj);
}
}
projectsToClose.remove(null);
projectsToOpen.remove(null);
OpenProjects.getDefault().close(projectsToClose.toArray(new Project[projectsToClose.size()]));
OpenProjects.getDefault().open(projectsToOpen.toArray(new Project[projectsToOpen.size()]), false);
}
@Override
public void createNewProject(File workingDir) {
Action newProjectAction = CommonProjectActions.newProjectAction();
if (newProjectAction != null) {
ProjectChooser.setProjectsFolder(workingDir);
newProjectAction.actionPerformed(new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, "command")); // NOI18N
}
}
@Override
public IDEProject getIDEProject(URL url) {
Project p = getProject(url);
return p != null ? createIDEProject(p) : null;
}
@Override
public IDEProject[] getOpenProjects() {
Project[] openProjects = OpenProjects.getDefault().getOpenProjects();
IDEProject[] ideProjects = new IDEProject[openProjects.length];
for (int i=0; i < openProjects.length; i++) {
ideProjects[i] = createIDEProject(openProjects[i]);
}
return ideProjects;
}
private static Project getProject(URL url) {
try {
return FileOwnerQuery.getOwner(url.toURI());
} catch (URISyntaxException ex) {
Exceptions.printStackTrace(ex);
return null;
}
}
private static IDEProject createIDEProject(Project p) {
ProjectInformation pi = ProjectUtils.getInformation(p);
return new NbProject(pi.getDisplayName(), pi.getIcon(), p.getProjectDirectory().toURL());
}
@Override
public synchronized void addProjectOpenListener(IDEProject.OpenListener listener) {
if (ideProjectOpenListeners == null) {
ideProjectOpenListeners = new LinkedList<Reference<IDEProject.OpenListener>>();
} else {
Iterator<Reference<IDEProject.OpenListener>> it = ideProjectOpenListeners.iterator();
while (it.hasNext()) {
Reference<IDEProject.OpenListener> r = it.next();
IDEProject.OpenListener l = r.get();
if (l == null || l == listener) {
it.remove(); // also doing cleanup of GC'ed references
}
}
}
ideProjectOpenListeners.add(new WeakReference<IDEProject.OpenListener>(listener));
if (projectOpenListener == null) {
projectOpenListener = new ProjectOpenListener();
OpenProjects.getDefault().addPropertyChangeListener(projectOpenListener);
}
}
@Override
public synchronized void removeProjectOpenListener(IDEProject.OpenListener listener) {
if (ideProjectOpenListeners != null) {
Iterator<Reference<IDEProject.OpenListener>> it = ideProjectOpenListeners.iterator();
while (it.hasNext()) {
Reference<IDEProject.OpenListener> r = it.next();
IDEProject.OpenListener l = r.get();
if (l == null || l == listener) {
it.remove(); // also doing cleanup of GC'ed references
}
}
if (ideProjectOpenListeners.isEmpty()) {
ideProjectOpenListeners = null;
if (projectOpenListener != null) {
OpenProjects.getDefault().removePropertyChangeListener(projectOpenListener);
projectOpenListener = null;
}
}
}
}
private static synchronized IDEProject.OpenListener[] getIDEProjectOpenListeners() {
if (ideProjectOpenListeners == null) {
return null;
}
List<IDEProject.OpenListener> listenerList = new ArrayList<IDEProject.OpenListener>(ideProjectOpenListeners.size());
Iterator<Reference<IDEProject.OpenListener>> it = ideProjectOpenListeners.iterator();
while (it.hasNext()) {
Reference<IDEProject.OpenListener> r = it.next();
IDEProject.OpenListener l = r.get();
if (l == null) {
it.remove(); // also doing cleanup of GC'ed references
} else {
listenerList.add(l);
}
}
if (ideProjectOpenListeners.isEmpty()) {
ideProjectOpenListeners = null;
if (projectOpenListener != null) {
OpenProjects.getDefault().removePropertyChangeListener(projectOpenListener);
projectOpenListener = null;
}
return null;
}
return listenerList.toArray(new IDEProject.OpenListener[listenerList.size()]);
}
private static class NbProject extends IDEProject {
private ProjectDeleteListener projectDeleteListener;
NbProject(String displayName, Icon icon, URL url) {
super(displayName, icon, url);
}
@Override
public synchronized boolean addDeleteListener(DeleteListener l) {
boolean added = super.addDeleteListener(l);
if (added && projectDeleteListener == null) {
Project p = getProject(getURL());
if (p != null) {
FileObject projDir = p.getProjectDirectory();
projectDeleteListener = new ProjectDeleteListener(projDir.toURL(), this);
projDir.addFileChangeListener(projectDeleteListener);
} else {
super.removeDeleteListener(l);
added = false;
}
}
return added;
}
@Override
public synchronized boolean removeDeleteListener(DeleteListener l) {
boolean removed = super.removeDeleteListener(l);
if (removed && getDeleteListeners().isEmpty() && projectDeleteListener != null) {
Project p = getProject(getURL());
if (p != null) {
FileObject projDir = p.getProjectDirectory();
projDir.removeFileChangeListener(projectDeleteListener);
projectDeleteListener = null;
}
}
return removed;
}
}
private static class ProjectOpenListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (!OpenProjects.PROPERTY_OPEN_PROJECTS.equals(evt.getPropertyName())
|| evt.getNewValue() == null) {
return;
}
IDEProject.OpenListener[] listeners = getIDEProjectOpenListeners();
if (listeners == null) {
return;
}
Project[] newProjects = (Project[])evt.getNewValue();
Project[] oldProjects = (Project[])evt.getOldValue();
List<Project> openedList;
if (oldProjects == null) {
openedList = Arrays.asList(newProjects);
} else {
openedList = new ArrayList<Project>();
openedList.addAll(Arrays.asList(newProjects));
openedList.removeAll(Arrays.asList(oldProjects));
}
if (!openedList.isEmpty()) {
IDEProject[] newlyOpened = new IDEProject[openedList.size()];
for (int i=0; i < newlyOpened.length; i++) {
newlyOpened[i] = createIDEProject(openedList.get(i));
}
for (IDEProject.OpenListener l : listeners) {
l.projectsOpened(newlyOpened);
}
}
}
}
private static class ProjectDeleteListener extends FileChangeAdapter {
private URL url;
private Reference<NbProject> projectRef;
ProjectDeleteListener(URL url, NbProject ideProject) {
this.url = url;
this.projectRef = new WeakReference<NbProject>(ideProject);
}
@Override
public void fileDeleted(FileEvent fe) {
if (fe.getFile().toURL().equals(url)) {
NbProject project = projectRef.get();
if (project != null) {
project.notifyDeleted();
} else {
fe.getFile().removeFileChangeListener(this);
}
}
}
}
}