001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.project.ui;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.lang.reflect.Field;
047: import java.net.MalformedURLException;
048: import java.net.URL;
049: import java.util.Collections;
050: import java.util.HashMap;
051: import java.util.HashSet;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.Set;
055: import java.util.SortedSet;
056: import java.util.TreeSet;
057: import java.util.concurrent.TimeUnit;
058: import java.util.concurrent.TimeoutException;
059: import java.util.logging.Level;
060: import java.util.regex.Matcher;
061: import java.util.regex.Pattern;
062: import org.netbeans.api.project.FileOwnerQuery;
063: import org.netbeans.api.project.Project;
064: import org.netbeans.api.project.ProjectManager;
065: import org.netbeans.api.project.ui.OpenProjects;
066: import org.netbeans.junit.Log;
067: import org.netbeans.junit.MockServices;
068: import org.netbeans.junit.NbTestCase;
069: import org.netbeans.modules.project.ui.actions.TestSupport;
070: import org.netbeans.spi.project.SubprojectProvider;
071: import org.netbeans.spi.project.ui.ProjectOpenedHook;
072: import org.openide.filesystems.FileObject;
073: import org.openide.filesystems.FileStateInvalidException;
074: import org.openide.filesystems.FileUtil;
075: import org.openide.filesystems.URLMapper;
076: import org.openide.loaders.DataObject;
077: import org.openide.loaders.DataObjectNotFoundException;
078: import org.openide.util.RequestProcessor;
079: import org.openide.util.lookup.Lookups;
080:
081: /** Tests fix of issue 56454.
082: *
083: * @author Jiri Rechtacek
084: */
085: public class OpenProjectListTest extends NbTestCase {
086: FileObject f1_1_open, f1_2_open, f1_3_close;
087: FileObject f2_1_open;
088:
089: Project project1, project2;
090: TestOpenCloseProjectDocument handler = new OpenProjectListTest.TestOpenCloseProjectDocument();
091:
092: public OpenProjectListTest(String testName) {
093: super (testName);
094: }
095:
096: protected void setUp() throws Exception {
097: super .setUp();
098: MockServices.setServices(TestSupport.TestProjectFactory.class);
099: clearWorkDir();
100:
101: ProjectUtilities.OPEN_CLOSE_PROJECT_DOCUMENT_IMPL = handler;
102:
103: FileObject workDir = FileUtil.toFileObject(getWorkDir());
104:
105: FileObject p1 = TestSupport.createTestProject(workDir,
106: "project1");
107: f1_1_open = p1.createData("f1_1.java");
108: f1_2_open = p1.createData("f1_2.java");
109: f1_3_close = p1.createData("f1_3.java");
110:
111: project1 = ProjectManager.getDefault().findProject(p1);
112: ((TestSupport.TestProject) project1).setLookup(Lookups
113: .singleton(TestSupport.createAuxiliaryConfiguration()));
114:
115: FileObject p2 = TestSupport.createTestProject(workDir,
116: "project2");
117: f2_1_open = p2.createData("f2_1.java");
118:
119: // project2 depends on projects1
120: project2 = ProjectManager.getDefault().findProject(p2);
121: ((TestSupport.TestProject) project2).setLookup(Lookups.fixed(
122: TestSupport.createAuxiliaryConfiguration(),
123: new MySubprojectProvider(project1)));
124:
125: // prepare set of open documents for both projects
126: ProjectUtilities.OPEN_CLOSE_PROJECT_DOCUMENT_IMPL
127: .open(f1_1_open);
128: ProjectUtilities.OPEN_CLOSE_PROJECT_DOCUMENT_IMPL
129: .open(f1_2_open);
130: ProjectUtilities.OPEN_CLOSE_PROJECT_DOCUMENT_IMPL
131: .open(f2_1_open);
132:
133: // close both projects with own open files
134: OpenProjectList.getDefault().close(
135: new Project[] { project1, project2 }, false);
136: }
137:
138: protected void tearDown() {
139: OpenProjectList.getDefault().close(
140: new Project[] { project1, project2 }, false);
141: }
142:
143: public void testOpen() throws Exception {
144: assertTrue("No project is open.", OpenProjectList.getDefault()
145: .getOpenProjects().length == 0);
146: CharSequence log = Log.enable("org.netbeans.ui", Level.FINE);
147: OpenProjectList.getDefault().open(project1, true);
148: assertTrue("Project1 is opened.", OpenProjectList.getDefault()
149: .isOpen(project1));
150: Pattern p = Pattern.compile("Opening.*1.*TestProject",
151: Pattern.MULTILINE | Pattern.DOTALL);
152: Matcher m = p.matcher(log);
153: if (!m.find()) {
154: fail("There should be TestProject\n" + log.toString());
155: }
156:
157: assertTrue("Document f1_1_open is loaded.", handler.openFiles
158: .contains(f1_1_open.getURL().toExternalForm()));
159: assertTrue("Document f1_2_open is loaded.", handler.openFiles
160: .contains(f1_2_open.getURL().toExternalForm()));
161: assertFalse("Document f2_1_open isn't loaded.",
162: handler.openFiles.contains(f2_1_open.getURL()
163: .toExternalForm()));
164: }
165:
166: public void testListenerOpenClose() throws Exception {
167: assertTrue("No project is open.", OpenProjectList.getDefault()
168: .getOpenProjects().length == 0);
169: ChangeListener list = new ChangeListener();
170: OpenProjectList.getDefault().addPropertyChangeListener(list);
171: CharSequence log = Log.enable("org.netbeans.ui", Level.FINE);
172: OpenProjectList.getDefault().open(project1, true);
173: Pattern p = Pattern.compile("Opening.*1.*TestProject",
174: Pattern.MULTILINE | Pattern.DOTALL);
175: Matcher m = p.matcher(log);
176: if (!m.find()) {
177: fail("There should be TestProject\n" + log.toString());
178: }
179: assertEquals(0, list.oldCount);
180: assertEquals(1, list.newCount);
181: OpenProjectList.getDefault().open(project2, true);
182: assertEquals(1, list.oldCount);
183: assertEquals(2, list.newCount);
184: OpenProjectList.getDefault().close(new Project[] { project1 },
185: false);
186: assertEquals(2, list.oldCount);
187: assertEquals(1, list.newCount);
188: OpenProjectList.getDefault().close(new Project[] { project2 },
189: false);
190: assertEquals(1, list.oldCount);
191: assertEquals(0, list.newCount);
192: }
193:
194: public void testClose() throws Exception {
195: testOpen();
196:
197: CharSequence log = Log.enable("org.netbeans.ui", Level.FINE);
198: OpenProjectList.getDefault().close(new Project[] { project1 },
199: false);
200: Pattern p = Pattern.compile("Closing.*1.*TestProject",
201: Pattern.MULTILINE | Pattern.DOTALL);
202: Matcher m = p.matcher(log);
203: if (!m.find()) {
204: fail("There should be TestProject\n" + log);
205: }
206: assertFalse("Document f1_1_open isn't loaded.",
207: handler.openFiles.contains(f1_1_open.getURL()
208: .toExternalForm()));
209: assertFalse("Document f1_2_open isn't loaded.",
210: handler.openFiles.contains(f1_2_open.getURL()
211: .toExternalForm()));
212: assertFalse("Document f2_1_open isn't loaded.",
213: handler.openFiles.contains(f2_1_open.getURL()
214: .toExternalForm()));
215:
216: OpenProjectList.getDefault().open(project1);
217: OpenProjectList.getDefault().open(project2);
218:
219: // close all project1's documents
220: handler.openFiles.remove(f1_1_open.getURL().toExternalForm());
221: handler.openFiles.remove(f1_2_open.getURL().toExternalForm());
222:
223: ProjectUtilities.closeAllDocuments(new Project[] { project1 },
224: false);
225: OpenProjectList.getDefault().close(new Project[] { project1 },
226: false);
227:
228: OpenProjectList.getDefault().open(project1);
229: assertFalse("Document f1_1_open isn't loaded.",
230: handler.openFiles.contains(f1_1_open.getURL()
231: .toExternalForm()));
232: assertFalse("Document f1_2_open isn't loaded.",
233: handler.openFiles.contains(f1_2_open.getURL()
234: .toExternalForm()));
235: assertTrue("Document f2_1_open is still loaded.",
236: handler.openFiles.contains(f2_1_open.getURL()
237: .toExternalForm()));
238: }
239:
240: public void testSerialize() throws Exception {
241: testOpen();
242:
243: OpenProjectList.waitProjectsFullyOpen();
244: Field f = OpenProjectList.class.getDeclaredField("INSTANCE");
245: f.setAccessible(true);
246: f.set(null, null);
247:
248: CharSequence whatIsLoggedWhenDeserializing = Log.enable(
249: "org.netbeans.ui", Level.FINE);
250:
251: Project[] arr = OpenProjectList.getDefault().getOpenProjects();
252: OpenProjectList.waitProjectsFullyOpen();
253: arr = OpenProjectList.getDefault().getOpenProjects();
254:
255: assertEquals("One", 1, arr.length);
256: Pattern p = Pattern.compile("Initializing.*1.*TestProject",
257: Pattern.MULTILINE | Pattern.DOTALL);
258: Matcher m = p.matcher(whatIsLoggedWhenDeserializing);
259: if (!m.find()) {
260: fail("There should be TestProject\n"
261: + whatIsLoggedWhenDeserializing);
262: }
263: }
264:
265: public void testOpenDependingProject() throws Exception {
266: assertTrue("No project is open.", OpenProjectList.getDefault()
267: .getOpenProjects().length == 0);
268: CharSequence log = Log.enable("org.netbeans.ui", Level.FINE);
269: OpenProjectList.getDefault().open(project2, true);
270: assertTrue("Project1 is opened.", OpenProjectList.getDefault()
271: .isOpen(project1));
272: assertTrue("Project2 is opened.", OpenProjectList.getDefault()
273: .isOpen(project2));
274: Pattern p = Pattern.compile("Opening.*2.*TestProject",
275: Pattern.MULTILINE | Pattern.DOTALL);
276: Matcher m = p.matcher(log);
277: if (!m.find()) {
278: fail("There should be TestProject\n" + log);
279: }
280:
281: assertTrue("Document f1_1_open is loaded.", handler.openFiles
282: .contains(f1_1_open.getURL().toExternalForm()));
283: assertTrue("Document f1_2_open is loaded.", handler.openFiles
284: .contains(f1_2_open.getURL().toExternalForm()));
285: assertTrue("Document f2_1_open is loaded.", handler.openFiles
286: .contains(f2_1_open.getURL().toExternalForm()));
287: }
288:
289: public void testCloseProjectWithoutOpenDocuments() throws Exception {
290: assertTrue("No project is open.", OpenProjectList.getDefault()
291: .getOpenProjects().length == 0);
292: OpenProjectList.getDefault().open(project2, false);
293: assertFalse("Project1 isn't opened.", OpenProjectList
294: .getDefault().isOpen(project1));
295: assertTrue("Project2 is opened.", OpenProjectList.getDefault()
296: .isOpen(project2));
297:
298: handler.openFiles.remove(f2_1_open.getURL().toExternalForm());
299:
300: assertFalse("Document f2_1_open isn't loaded.",
301: handler.openFiles.contains(f2_1_open.getURL()
302: .toExternalForm()));
303:
304: ProjectUtilities.closeAllDocuments(new Project[] { project2 },
305: false);
306: OpenProjectList.getDefault().close(new Project[] { project2 },
307: false);
308:
309: assertFalse("Project2 is closed.", OpenProjectList.getDefault()
310: .isOpen(project2));
311: }
312:
313: public void testProjectOpenedClosed() throws Exception {
314: ((TestSupport.TestProject) project1).setLookup(Lookups
315: .fixed(new Object[] { new TestProjectOpenedHookImpl(),
316: new TestProjectOpenedHookImpl(), }));
317:
318: TestProjectOpenedHookImpl.opened = 0;
319: TestProjectOpenedHookImpl.closed = 0;
320:
321: OpenProjectList.getDefault().open(project1);
322:
323: assertEquals("both open hooks were called", 2,
324: TestProjectOpenedHookImpl.opened);
325: assertEquals("no close hook was called", 0,
326: TestProjectOpenedHookImpl.closed);
327:
328: OpenProjectList.getDefault().close(new Project[] { project1 },
329: false);
330:
331: assertEquals("both open hooks were called", 2,
332: TestProjectOpenedHookImpl.opened);
333: assertEquals("both close hooks were called", 2,
334: TestProjectOpenedHookImpl.closed);
335: }
336:
337: public void testNotifyDeleted() throws Exception {
338: FileObject workDir = FileUtil.toFileObject(getWorkDir());
339:
340: FileObject p1 = workDir.createFolder("p1");
341: FileObject p1TestProject = p1.createFolder("testproject");
342:
343: Project project1 = ProjectManager.getDefault().findProject(p1);
344:
345: assertNotNull("project1 is recognized", project1);
346:
347: OpenProjectList.getDefault().open(project1);
348:
349: OpenProjectList.getDefault().close(new Project[] { project1 },
350: false);
351:
352: p1TestProject.delete();
353: TestSupport.notifyDeleted(project1);
354:
355: assertNull("project1 is deleted", ProjectManager.getDefault()
356: .findProject(p1));
357:
358: assertFalse("project1 is not in recent projects list",
359: OpenProjectList.getDefault().getRecentProjects()
360: .contains(project1));
361:
362: FileObject p2 = workDir.createFolder("p2");
363: FileObject p2TestProject = p2.createFolder("testproject");
364:
365: Project project2 = ProjectManager.getDefault().findProject(p2);
366:
367: assertNotNull("project2 is recognized", project2);
368: OpenProjectList.getDefault().open(project2);
369:
370: OpenProjectList.getDefault().close(new Project[] { project2 },
371: false);
372:
373: TestSupport.notifyDeleted(project2);
374:
375: assertFalse("project2 is not in recent projects list",
376: OpenProjectList.getDefault().getRecentProjects()
377: .contains(project2));
378: }
379:
380: public void testMainProject() throws Exception {
381: FileObject workDir = FileUtil.toFileObject(getWorkDir());
382:
383: FileObject p1 = workDir.createFolder("p1");
384: FileObject p1TestProject = p1.createFolder("testproject");
385:
386: Project project1 = ProjectManager.getDefault().findProject(p1);
387:
388: assertNotNull("project1 is recognized", project1);
389:
390: FileObject p2 = workDir.createFolder("p2");
391: FileObject p2TestProject = p2.createFolder("testproject");
392:
393: Project project2 = ProjectManager.getDefault().findProject(p2);
394:
395: assertNotNull("project2 is recognized", project2);
396:
397: FileObject p3 = workDir.createFolder("p3");
398: FileObject p3TestProject = p3.createFolder("testproject");
399:
400: Project project3 = ProjectManager.getDefault().findProject(p3);
401:
402: assertNotNull("project3 is recognized", project3);
403:
404: assertNull("no main project set when OPL is empty",
405: OpenProjectList.getDefault().getMainProject());
406:
407: OpenProjectList.getDefault().open(project1);
408:
409: assertNull("open project does not change main project",
410: OpenProjectList.getDefault().getMainProject());
411:
412: OpenProjectList.getDefault().setMainProject(project1);
413:
414: assertTrue("main project correctly set", OpenProjectList
415: .getDefault().getMainProject() == project1);
416:
417: OpenProjectList.getDefault().open(project2);
418:
419: assertTrue(
420: "open project does not change main project",
421: OpenProjectList.getDefault().getMainProject() == project1);
422:
423: OpenProjectList.getDefault().close(new Project[] { project1 },
424: false);
425:
426: assertNull("no main project set when main project is closed",
427: OpenProjectList.getDefault().getMainProject());
428:
429: boolean exceptionThrown = false;
430:
431: try {
432: OpenProjectList.getDefault().setMainProject(project3);
433: } catch (IllegalArgumentException e) {
434: exceptionThrown = true;
435: }
436:
437: assertTrue(
438: "IAE thrown when trying to set main project that is not opened",
439: exceptionThrown);
440:
441: //the same for a previously opened project:
442: exceptionThrown = false;
443:
444: try {
445: OpenProjectList.getDefault().setMainProject(project1);
446: } catch (IllegalArgumentException e) {
447: exceptionThrown = true;
448: }
449:
450: assertTrue(
451: "IAE thrown when trying to set main project that is not opened",
452: exceptionThrown);
453: }
454:
455: // helper code
456:
457: private static class MySubprojectProvider implements
458: SubprojectProvider {
459: Project p;
460:
461: public MySubprojectProvider(final Project project) {
462: p = project;
463: }
464:
465: public Set<Project> getSubprojects() {
466: return Collections.singleton(p);
467: }
468:
469: public void removeChangeListener(
470: javax.swing.event.ChangeListener changeListener) {
471: }
472:
473: public void addChangeListener(
474: javax.swing.event.ChangeListener changeListener) {
475: }
476:
477: }
478:
479: private static class TestOpenCloseProjectDocument implements
480: ProjectUtilities.OpenCloseProjectDocument {
481: public Set<String> openFiles = new HashSet<String>();
482: public Map<Project, SortedSet<String>> urls4project = new HashMap<Project, SortedSet<String>>();
483:
484: public boolean open(FileObject fo) {
485: Project owner = FileOwnerQuery.getOwner(fo);
486: if (!urls4project.containsKey(owner)) {
487: // add project
488: urls4project.put(owner, new TreeSet<String>());
489: }
490: URL url = null;
491: DataObject dobj = null;
492: try {
493: dobj = DataObject.find(fo);
494: url = dobj.getPrimaryFile().getURL();
495: urls4project.get(owner).add(url.toExternalForm());
496: openFiles.add(fo.getURL().toExternalForm());
497: } catch (FileStateInvalidException fsie) {
498: fail("FileStateInvalidException in "
499: + dobj.getPrimaryFile());
500: } catch (DataObjectNotFoundException donfe) {
501: fail("DataObjectNotFoundException on " + fo);
502: }
503: return true;
504: }
505:
506: public Map<Project, SortedSet<String>> close(
507: Project[] projects, boolean notifyUI) {
508:
509: for (int i = 0; i < projects.length; i++) {
510: SortedSet<String> projectOpenFiles = urls4project
511: .get(projects[i]);
512: if (projectOpenFiles != null) {
513: projectOpenFiles.retainAll(openFiles);
514: urls4project.put(projects[i], projectOpenFiles);
515: for (String url : projectOpenFiles) {
516: FileObject fo = null;
517: try {
518: fo = URLMapper.findFileObject(new URL(url));
519: openFiles.remove(fo.getURL()
520: .toExternalForm());
521: } catch (MalformedURLException mue) {
522: fail("MalformedURLException in " + url);
523: } catch (FileStateInvalidException fsie) {
524: fail("FileStateInvalidException in " + fo);
525: }
526: }
527: }
528: }
529:
530: return urls4project;
531: }
532: }
533:
534: private static class TestProjectOpenedHookImpl extends
535: ProjectOpenedHook implements Runnable {
536:
537: public static int opened = 0;
538: public static int closed = 0;
539:
540: private Object result;
541:
542: protected void projectClosed() {
543: closed++;
544: assertFalse("Working on", OpenProjects.getDefault()
545: .openProjects().isDone());
546: RequestProcessor.getDefault().post(this ).waitFinished();
547: assertNotNull("some result computed", result);
548: assertEquals("It is time out exception",
549: TimeoutException.class, result.getClass());
550: }
551:
552: protected void projectOpened() {
553: opened++;
554: assertFalse("Working on", OpenProjects.getDefault()
555: .openProjects().isDone());
556: RequestProcessor.getDefault().post(this ).waitFinished();
557: assertNotNull("some result computed", result);
558: assertEquals("It is time out exception",
559: TimeoutException.class, result.getClass());
560: }
561:
562: public void run() {
563: try {
564: result = OpenProjects.getDefault().openProjects().get(
565: 100, TimeUnit.MILLISECONDS);
566: } catch (Exception ex) {
567: result = ex;
568: }
569: }
570: }
571:
572: private class ChangeListener implements PropertyChangeListener {
573: int oldCount = -1;
574: int newCount = -1;
575:
576: public void propertyChange(PropertyChangeEvent arg0) {
577: if (OpenProjectList.PROPERTY_OPEN_PROJECTS.equals(arg0
578: .getPropertyName())) {
579: Object old = arg0.getOldValue();
580: Object nw = arg0.getNewValue();
581: assertNotNull(old);
582: assertNotNull(nw);
583: Project[] oList = (Project[]) old;
584: Project[] nList = (Project[]) nw;
585: oldCount = oList.length;
586: newCount = nList.length;
587: }
588: }
589: }
590: }
|