diff -r f4a875afac55 projectui/src/org/netbeans/modules/project/ui/OpenProjectList.java
--- a/projectui/src/org/netbeans/modules/project/ui/OpenProjectList.java Wed Dec 03 10:10:50 2008 +0100
+++ b/projectui/src/org/netbeans/modules/project/ui/OpenProjectList.java Wed Dec 03 10:05:49 2008 +0100
@@ -91,6 +91,7 @@
import org.netbeans.modules.project.uiapi.ProjectOpenedTrampoline;
import org.netbeans.spi.project.SubprojectProvider;
import org.netbeans.spi.project.ui.PrivilegedTemplates;
+import org.netbeans.spi.project.ui.ProjectInitializationHook;
import org.netbeans.spi.project.ui.ProjectOpenedHook;
import org.netbeans.spi.project.ui.RecommendedTemplates;
import org.openide.ErrorManager;
@@ -220,6 +221,30 @@
}
}
return wrap;
+ }
+
+ private static Project doInitializeProject(Project p) {
+ ClassLoader l = ProjectOpenedHook.class.getClassLoader();
+ try {
+ Class.forName(ProjectOpenedHook.class.getName(), true, l);
+ } catch (ClassNotFoundException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ INIT: for (;;) {
+ Collection extends ProjectInitializationHook> initHooks = p.getLookup().lookupAll(ProjectInitializationHook.class);
+ assert initHooks.size() <= 1 : "There can be at most one hook " + initHooks;
+ for (ProjectInitializationHook h : initHooks) {
+ Project np = ProjectOpenedTrampoline.DEFAULT.projectWillBeOpened(h);
+ if (np == null) {
+ return np;
+ }
+ if (np != p) {
+ p = np;
+ continue INIT;
+ }
+ }
+ return p;
+ }
}
/** Modifications to the recentTemplates variables shall be done only
@@ -352,6 +377,12 @@
}
p = toOpenProjects.remove();
LOGGER.log(Level.FINER, "after remove {0}", toOpenProjects); // NOI18N
+ }
+ LOGGER.log(Level.FINE, "about to init a project {0}", p); // NOI18N
+ p = doInitializeProject(p);
+ if (p == null) {
+ LOGGER.log(Level.FINE, "project shall not be opened"); // NOI18N
+ continue;
}
LOGGER.log(Level.FINE, "about to open a project {0}", p); // NOI18N
if (notifyOpened(p)) {
@@ -1031,9 +1062,14 @@
}
}
- private boolean doOpenProject(final Project p) {
+ private boolean doOpenProject(Project p) {
boolean recentProjectsChanged;
LOGGER.finer("doOpenProject(): opening project " + p.toString());
+ p = doInitializeProject(p);
+ if (p == null) {
+ return false;
+ }
+
synchronized (this) {
if (openProjects.contains(p)) {
return false;
@@ -1049,12 +1085,12 @@
logProjects("doOpenProject(): openProjects == ", openProjects.toArray(new Project[0])); // NOI18N
// Notify projects opened
notifyOpened(p);
-
+
+ final Project openFilesFor = p;
Mutex.EVENT.readAccess(new Action() {
public Void run() {
// Open project files
- ProjectUtilities.openProjectFiles(p);
-
+ ProjectUtilities.openProjectFiles(openFilesFor);
return null;
}
});
diff -r f4a875afac55 projectui/test/unit/src/org/netbeans/modules/project/ui/OpenProjectInitializeTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projectui/test/unit/src/org/netbeans/modules/project/ui/OpenProjectInitializeTest.java Wed Dec 03 10:05:50 2008 +0100
@@ -0,0 +1,274 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 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-2006 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ *
+ * 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.netbeans.modules.project.ui;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.project.ui.OpenProjects;
+import org.netbeans.junit.MockServices;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.spi.project.ProjectFactory;
+import org.netbeans.spi.project.ProjectState;
+import org.netbeans.spi.project.ui.ProjectInitializationHook;
+import org.netbeans.spi.project.ui.ProjectOpenedHook;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.lookup.AbstractLookup;
+import org.openide.util.lookup.InstanceContent;
+
+/** Verify ProjectInitializationHook is called.
+ *
+ * @author Jaroslav Tulach
+ */
+public class OpenProjectInitializeTest extends NbTestCase {
+ FileObject f1_1_open, f1_2_open, f1_3_close;
+ FileObject f2_1_open;
+
+ Project project1;
+
+ public OpenProjectInitializeTest (String testName) {
+ super (testName);
+ }
+
+ @Override
+ protected Level logLevel() {
+ return Level.FINE;
+ }
+
+ @Override
+ protected void setUp () throws Exception {
+ super.setUp ();
+ MockServices.setServices(MyFactory.class);
+ clearWorkDir ();
+
+ FileObject workDir = FileUtil.toFileObject (getWorkDir ());
+
+ FileObject p1 = workDir.createFolder("prjDir");
+ f1_1_open = p1.createData("f1_1.java");
+ f1_2_open = p1.createData("f1_2.java");
+ f1_3_close = p1.createData("f1_3.java");
+
+ MyFactory.toRet = new MyProject(p1);
+ project1 = ProjectManager.getDefault ().findProject (p1);
+ assertNotNull("Project found", project1);
+
+ assertEquals("No open prjs", 0, OpenProjects.getDefault().openProjects().get().length);
+ }
+
+ @Override
+ protected void tearDown () throws Exception {
+ OpenProjectList.getDefault().close(OpenProjectList.getDefault().getOpenProjects(), false);
+ assertEquals("No open prjs", 0, OpenProjects.getDefault().openProjects().get().length);
+ }
+
+ public void testOpenWithOneRedirect() throws Exception {
+ assertTrue ("No project is open.", OpenProjectList.getDefault ().getOpenProjects ().length == 0);
+
+ MyProject project2 = new MyProject(project1.getProjectDirectory());
+
+ InitHook h = new InitHook(project2);
+ MyProject tp = (MyProject)project1;
+ h.stateToClose = tp.state;
+ tp.ic.add(h);
+
+ OpenProjectList.getDefault ().open(tp, true);
+
+ Project[] open = OpenProjectList.getDefault().getOpenProjects();
+ assertEquals("One is open", 1, open.length);
+ assertEquals("Redirect gets opened", project2, open[0]);
+ assertEquals("Its open hook is called", 1, project2.openHook.opened);
+ assertEquals("Open hook of project1 ignored", 0, tp.openHook.opened);
+ }
+
+ public void testOpenWithTwoRedirect() throws Exception {
+ assertTrue ("No project is open.", OpenProjectList.getDefault ().getOpenProjects ().length == 0);
+
+ MyProject project2 = new MyProject(project1.getProjectDirectory());
+ MyProject project3 = new MyProject(project1.getProjectDirectory());
+
+ InitHook h = new InitHook(project2);
+ InitHook h2 = new InitHook(project3);
+ MyProject tp = (MyProject)project1;
+ h2.stateToClose = tp.state;
+ tp.ic.add(h2);
+ project2.ic.add(h2);
+
+ OpenProjectList.getDefault ().open (tp, true);
+
+ Project[] open = OpenProjectList.getDefault().getOpenProjects();
+ assertEquals("One is open", 1, open.length);
+ assertEquals("Project3 gets opened", project3, open[0]);
+ assertEquals("Project3 open hook is called", 1, project3.openHook.opened);
+ assertEquals("Project2 open hook is ignored", 0, project2.openHook.opened);
+ assertEquals("Open hook of project1 ignored", 0, tp.openHook.opened);
+ }
+
+ public void testNullOpensNoProject() throws Exception {
+ assertTrue ("No project is open.", OpenProjectList.getDefault ().getOpenProjects ().length == 0);
+
+ InitHook h = new InitHook(null);
+ MyProject tp = (MyProject)project1;
+ tp.ic.add(h);
+
+ OpenProjectList.getDefault ().open (tp, true);
+
+ Project[] open = OpenProjectList.getDefault().getOpenProjects();
+ assertEquals("Nothing is open", 0, open.length);
+ assertEquals("Open hook of project1 ignored", 0, tp.openHook.opened);
+ }
+
+ public static class MyFactory implements ProjectFactory {
+ static MyProject toRet;
+
+ public boolean isProject(FileObject d) {
+ return toRet != null && toRet.getProjectDirectory().equals(d);
+ }
+
+ public Project loadProject(FileObject projectDirectory, ProjectState state) throws IOException {
+ if (toRet != null && projectDirectory.equals(toRet.getProjectDirectory())) {
+ MyProject h = toRet;
+ h.state = state;
+ toRet = null;
+ return h;
+ }
+ return null;
+ }
+
+ public void saveProject(Project project) throws IOException, ClassCastException {
+ }
+ } // end of MyFactory
+
+
+
+ private static class MyProject implements Project {
+ final FileObject fo;
+ final Lookup lookup;
+ final InstanceContent ic;
+ final OpenHook openHook;
+ ProjectState state;
+
+ public MyProject(FileObject fo) {
+ this.fo = fo;
+ this.ic = new InstanceContent();
+ this.lookup = new AbstractLookup(ic);
+ this.openHook = new OpenHook();
+ ic.add(openHook);
+ }
+
+ public FileObject getProjectDirectory() {
+ return fo;
+ }
+
+ public Lookup getLookup() {
+ return lookup;
+ }
+ } // end of MyProject
+ private static class InitHook extends ProjectInitializationHook {
+ MyProject willBeOpened;
+ ProjectState stateToClose;
+ public InitHook(MyProject next) {
+ willBeOpened = next;
+ }
+
+ @Override
+ protected Project projectWillBeOpened() {
+ if (stateToClose != null) {
+ stateToClose.notifyDeleted();
+ stateToClose = null;
+ }
+ if (willBeOpened == null) {
+ return null;
+ }
+ try {
+ MyFactory.toRet = willBeOpened;
+ Project found = ProjectManager.getDefault().findProject(willBeOpened.getProjectDirectory());
+ assertEquals("The right one found", willBeOpened, found);
+ return found;
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ } catch (IllegalArgumentException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return null;
+ }
+ } // end of InitHook
+ private static class OpenHook extends ProjectOpenedHook
+ implements Runnable {
+ int opened = 0;
+ int closed = 0;
+
+ private Object result;
+
+ protected void projectClosed() {
+ closed++;
+ assertFalse("Working on", OpenProjects.getDefault().openProjects().isDone());
+ RequestProcessor.getDefault().post(this).waitFinished();
+ assertNotNull("some result computed", result);
+ assertEquals("It is time out exception", TimeoutException.class, result.getClass());
+ }
+
+ protected void projectOpened() {
+ opened++;
+ assertFalse("Working on", OpenProjects.getDefault().openProjects().isDone());
+ RequestProcessor.getDefault().post(this).waitFinished();
+ assertNotNull("some result computed", result);
+ assertEquals("It is time out exception", TimeoutException.class, result.getClass());
+ }
+
+ public void run() {
+ try {
+ result = OpenProjects.getDefault().openProjects().get(100, TimeUnit.MILLISECONDS);
+ } catch (Exception ex) {
+ result = ex;
+ }
+ }
+ }
+
+
+}
diff -r f4a875afac55 projectuiapi/apichanges.xml
--- a/projectuiapi/apichanges.xml Wed Dec 03 10:10:50 2008 +0100
+++ b/projectuiapi/apichanges.xml Wed Dec 03 10:05:50 2008 +0100
@@ -104,6 +104,23 @@
+
+
+ Add ProjectInitializationHook
+
+
+
+
+
+
+ ProjectInitializationHook
can be provided by Project's
+ to be notified that the project is about to become opened and
+ prevent that by finding proper substitute.
+
+
+
+
+
Add annotation @NodeFactory.Registration
diff -r f4a875afac55 projectuiapi/nbproject/project.properties
--- a/projectuiapi/nbproject/project.properties Wed Dec 03 10:10:50 2008 +0100
+++ b/projectuiapi/nbproject/project.properties Wed Dec 03 10:05:50 2008 +0100
@@ -39,7 +39,7 @@
javac.compilerargs=-Xlint -Xlint:-serial
javac.source=1.5
-spec.version.base=1.33.0
+spec.version.base=1.34.0
is.autoload=true
javadoc.arch=${basedir}/arch.xml
javadoc.apichanges=${basedir}/apichanges.xml
diff -r f4a875afac55 projectuiapi/src/org/netbeans/modules/project/uiapi/ProjectOpenedTrampoline.java
--- a/projectuiapi/src/org/netbeans/modules/project/uiapi/ProjectOpenedTrampoline.java Wed Dec 03 10:10:50 2008 +0100
+++ b/projectuiapi/src/org/netbeans/modules/project/uiapi/ProjectOpenedTrampoline.java Wed Dec 03 10:05:50 2008 +0100
@@ -41,6 +41,8 @@
package org.netbeans.modules.project.uiapi;
+import org.netbeans.api.project.Project;
+import org.netbeans.spi.project.ui.ProjectInitializationHook;
import org.netbeans.spi.project.ui.ProjectOpenedHook;
/**
@@ -62,6 +64,9 @@
/** Used by {@link ProjectOpenedHook}. */
protected ProjectOpenedTrampoline() {}
+
+ /** Delegates to {@link ProjectInitializationHook#projectWillBeOpened()}. */
+ public abstract Project projectWillBeOpened(ProjectInitializationHook hook);
/** Delegates to {@link ProjectOpenedHook#projectOpened}. */
public abstract void projectOpened(ProjectOpenedHook hook);
diff -r f4a875afac55 projectuiapi/src/org/netbeans/spi/project/ui/ProjectInitializationHook.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projectuiapi/src/org/netbeans/spi/project/ui/ProjectInitializationHook.java Wed Dec 03 10:05:50 2008 +0100
@@ -0,0 +1,76 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 1997-2007 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-2006 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ *
+ * 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.netbeans.spi.project.ui;
+
+import org.netbeans.api.project.Project;
+
+/**
+ * A hook which can be run when a project is about to be "opened" the GUI.
+ * Called before {@link ProjectOpenedHook}'s projectOpened
method
+ * is called. Register an instance of this class into {@link Project#getLookup()}
+ * method to perform pre-initialization actions, including finding another
+ * project to represent this directory.
+ *
+ * @see ProjectOpenedHook#projectOpened
+ * @see org.netbeans.api.project.Project#getLookup
+ *
+ * @author Jaroslav Tulach
+ * @since 1.34
+ */
+public abstract class ProjectInitializationHook {
+ /**
+ * Default constructor for use by subclasses.
+ */
+ protected ProjectInitializationHook() {}
+
+ /**
+ * Called when a project is about to be open in the GUI. The project has
+ * a chance to initialize necessary system resources and potentially
+ * also provide a delegate that shall be open instead of the project
+ * itself.
+ *
+ * @return either project that shall be installed instead of the one
+ * providing this hook, the project itself, or
+ * null
which means that no project will be opened at all
+ */
+ protected abstract Project projectWillBeOpened();
+}
diff -r f4a875afac55 projectuiapi/src/org/netbeans/spi/project/ui/ProjectOpenedHook.java
--- a/projectuiapi/src/org/netbeans/spi/project/ui/ProjectOpenedHook.java Wed Dec 03 10:10:50 2008 +0100
+++ b/projectuiapi/src/org/netbeans/spi/project/ui/ProjectOpenedHook.java Wed Dec 03 10:05:50 2008 +0100
@@ -41,6 +41,7 @@
package org.netbeans.spi.project.ui;
+import org.netbeans.api.project.Project;
import org.netbeans.modules.project.uiapi.ProjectOpenedTrampoline;
/**
@@ -82,6 +83,11 @@
}
public void projectClosed(ProjectOpenedHook hook) {
hook.projectClosed();
+ }
+
+ @Override
+ public Project projectWillBeOpened(ProjectInitializationHook hook) {
+ return hook.projectWillBeOpened();
}
};
}