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.junit.ide;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.io.File;
047: import java.io.IOException;
048: import java.util.Iterator;
049: import org.netbeans.api.java.source.SourceUtils;
050: import org.netbeans.api.project.FileOwnerQuery;
051: import org.openide.ErrorManager;
052: import org.netbeans.api.project.Project;
053: import org.netbeans.api.project.ProjectUtils;
054: import org.netbeans.modules.java.j2seproject.J2SEProjectGenerator;
055: import org.netbeans.modules.project.ui.OpenProjectList;
056: import org.openide.filesystems.FileObject;
057: import org.openide.loaders.DataObject;
058: import org.openide.util.Mutex;
059:
060: /** A helper class to work with projects in test cases. It is packed to
061: * nbjunit-ide.jar. This jar is added to compile classpath in module_harness.xml
062: * and it is added to system class loader classpath in ide_plugin_targets.xml.
063: * Then it ican be used in test cases.
064: */
065: public class ProjectSupport {
066:
067: /** This class is just a helper class and it should not be instantiated. */
068: private ProjectSupport() {
069: throw new UnsupportedOperationException(
070: "It is just a helper class.");
071: }
072:
073: /** Opens project in specified directory.
074: * @param projectDir a directory with project to open
075: * @return Project instance of opened project
076: */
077: public static Object openProject(File projectDir) {
078: final ProjectListListener listener = new ProjectListListener();
079: try {
080: // open project
081: final Project project = OpenProjectList
082: .fileToProject(projectDir);
083: if (project == null) {
084: ErrorManager.getDefault().log(ErrorManager.USER,
085: "Project not found: " + projectDir);
086: return null;
087: }
088: // posting the to AWT event thread
089: Mutex.EVENT.writeAccess(new Runnable() {
090: public void run() {
091: OpenProjectList.getDefault()
092: .addPropertyChangeListener(listener);
093: OpenProjectList.getDefault().open(project);
094: // Set main? Probably user should do this if he wants.
095: // OpenProjectList.getDefault().setMainProject(project);
096: }
097: });
098: // WAIT PROJECT OPEN - start
099: // We need to wait until project is open and then we can start to
100: // wait when scanning finishes. If we don't wait, scanning is started
101: // too early and finishes immediatelly.
102: Thread waitThread = new Thread(new Runnable() {
103: public void run() {
104: while (!listener.projectListChanged) {
105: try {
106: Thread.sleep(50);
107: } catch (Exception e) {
108: ErrorManager.getDefault().notify(
109: ErrorManager.EXCEPTION, e);
110: }
111: }
112: }
113: });
114: waitThread.start();
115: try {
116: waitThread.join(60000L); // wait 1 minute at the most
117: } catch (InterruptedException iex) {
118: ErrorManager.getDefault().notify(
119: ErrorManager.EXCEPTION, iex);
120: }
121: if (waitThread.isAlive()) {
122: // time-out expired, project not opened -> interrupt the wait thread
123: ErrorManager.getDefault().log(ErrorManager.USER,
124: "Project not opened in 60 second.");
125: waitThread.interrupt();
126: return null;
127: }
128: // WAIT PROJECT OPEN - end
129: // wait until metadata scanning is finished
130: waitScanFinished();
131: return project;
132: } catch (Exception ex) {
133: ErrorManager.getDefault()
134: .notify(ErrorManager.EXCEPTION, ex);
135: return null;
136: } finally {
137: OpenProjectList.getDefault().removePropertyChangeListener(
138: listener);
139: }
140: }
141:
142: /** Opens project on specified path.
143: * @param projectPath path to a directory with project to open
144: * @return Project instance of opened project
145: */
146: public static Object openProject(String projectPath) {
147: return openProject(new File(projectPath));
148: }
149:
150: /** Creates an empty Java project in specified directory and opens it.
151: * Its name is defined by name parameter.
152: * @param projectParentPath path to directory where to create name subdirectory and
153: * new project structure in that subdirectory.
154: * @param name name of the project
155: * @return Project instance of created project
156: */
157: public static Object createProject(String projectParentPath,
158: String name) {
159: return createProject(new File(projectParentPath), name);
160: }
161:
162: /** Creates an empty Java project in specified directory and opens it.
163: * Its name is defined by name parameter.
164: * @param projectParentDir directory where to create name subdirectory and
165: * new project structure in that subdirectory.
166: * @param name name of the project
167: * @return Project instance of created project
168: */
169: public static Object createProject(File projectParentDir,
170: String name) {
171: String mainClass = null;
172: try {
173: File projectDir = new File(projectParentDir, name);
174: J2SEProjectGenerator.createProject(projectDir, name,
175: mainClass, null, null);
176: return openProject(projectDir);
177: } catch (IOException e) {
178: ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, e);
179: return null;
180: }
181: }
182:
183: /** Closes project with system or display name equals to given name.
184: * It also closes all files open in editor. If a file is modified, all
185: * changes are discarded.
186: * @param name system or display name of project to be closed.
187: * @return true if project is closed, false otherwise (i.e. project was
188: * not found).
189: */
190: public static boolean closeProject(String name) {
191: Project[] projects = OpenProjectList.getDefault()
192: .getOpenProjects();
193: for (int i = 0; i < projects.length; i++) {
194: final Project project = projects[i];
195: if (ProjectUtils.getInformation(project).getDisplayName()
196: .equals(name)
197: || ProjectUtils.getInformation(project).getName()
198: .equals(name)) {
199: final ProjectListListener listener = new ProjectListListener();
200: // posting the to AWT event thread
201: Mutex.EVENT.writeAccess(new Runnable() {
202: public void run() {
203: discardChanges(project);
204: OpenProjectList.getDefault()
205: .addPropertyChangeListener(listener);
206: OpenProjectList.getDefault().close(
207: new Project[] { project }, true);
208: }
209: });
210: // WAIT PROJECT CLOSED - start
211: Thread waitThread = new Thread(new Runnable() {
212: public void run() {
213: while (!listener.projectListChanged) {
214: try {
215: Thread.sleep(50);
216: } catch (Exception e) {
217: ErrorManager.getDefault().notify(
218: ErrorManager.EXCEPTION, e);
219: }
220: }
221: }
222: });
223: waitThread.start();
224: try {
225: waitThread.join(60000L); // wait 1 minute at the most
226: } catch (InterruptedException iex) {
227: ErrorManager.getDefault().notify(
228: ErrorManager.EXCEPTION, iex);
229: }
230: if (waitThread.isAlive()) {
231: // time-out expired, project not opened -> interrupt the wait thread
232: ErrorManager.getDefault().log(ErrorManager.USER,
233: "Project not closed in 60 second.");
234: waitThread.interrupt();
235: return false;
236: }
237: // WAIT PROJECT CLOSED - end
238: return true;
239: }
240: }
241: // project not found
242: return false;
243: }
244:
245: /** Discards all changes in modified files of given project. */
246: private static void discardChanges(Project project) {
247: // discard all changes in modified files
248: Iterator iter = DataObject.getRegistry().getModifiedSet()
249: .iterator();
250: while (iter.hasNext()) {
251: DataObject dobj = (DataObject) iter.next();
252: if (dobj != null) {
253: FileObject fobj = dobj.getPrimaryFile();
254: Project owner = FileOwnerQuery.getOwner(fobj);
255: if (owner == project) {
256: dobj.setModified(false);
257: }
258: }
259: }
260: }
261:
262: /** Waits until metadata scanning is finished. */
263: public static void waitScanFinished() {
264: try {
265: SourceUtils.waitScanFinished();
266: } catch (InterruptedException ex) {
267: ErrorManager.getDefault()
268: .notify(ErrorManager.EXCEPTION, ex);
269: }
270: }
271:
272: /** Listener for project open. */
273: static class ProjectListListener implements PropertyChangeListener {
274: public boolean projectListChanged = false;
275:
276: /** Listen for property which changes when project is hopefully opened. */
277: public void propertyChange(PropertyChangeEvent evt) {
278: if (OpenProjectList.PROPERTY_OPEN_PROJECTS.equals(evt
279: .getPropertyName())) {
280: projectListChanged = true;
281: }
282: }
283: }
284: }
|