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.apisupport.project.ui;
043:
044: import java.awt.Dialog;
045: import java.awt.Image;
046: import java.awt.event.ActionEvent;
047: import java.awt.event.ActionListener;
048: import java.io.File;
049: import java.io.IOException;
050: import java.net.URL;
051: import java.util.ArrayList;
052: import java.util.Arrays;
053: import java.util.Collections;
054: import java.util.HashMap;
055: import java.util.HashSet;
056: import java.util.LinkedHashSet;
057: import java.util.List;
058: import java.util.Map;
059: import java.util.Set;
060: import java.util.SortedSet;
061: import java.util.TreeSet;
062: import javax.swing.AbstractAction;
063: import javax.swing.Action;
064: import javax.swing.Icon;
065: import javax.swing.ImageIcon;
066: import org.netbeans.api.project.ProjectManager;
067: import org.netbeans.modules.apisupport.project.NbModuleProject;
068: import org.netbeans.modules.apisupport.project.ProjectXMLManager;
069: import org.netbeans.modules.apisupport.project.Util;
070: import org.netbeans.modules.apisupport.project.ui.customizer.AddModulePanel;
071: import org.netbeans.modules.apisupport.project.ui.customizer.EditTestDependencyPanel;
072: import org.netbeans.modules.apisupport.project.ui.customizer.ModuleDependency;
073: import org.netbeans.modules.apisupport.project.ui.customizer.SingleModuleProperties;
074: import org.netbeans.modules.apisupport.project.universe.ModuleEntry;
075: import org.netbeans.modules.apisupport.project.universe.TestModuleDependency;
076: import org.netbeans.spi.java.project.support.ui.PackageView;
077: import org.netbeans.spi.project.support.ant.AntProjectEvent;
078: import org.netbeans.spi.project.support.ant.AntProjectHelper;
079: import org.netbeans.spi.project.support.ant.AntProjectListener;
080: import org.openide.DialogDescriptor;
081: import org.openide.DialogDisplayer;
082: import org.openide.ErrorManager;
083: import org.openide.actions.FindAction;
084: import org.openide.filesystems.FileObject;
085: import org.openide.filesystems.URLMapper;
086: import org.openide.nodes.AbstractNode;
087: import org.openide.nodes.Children;
088: import org.openide.nodes.FilterNode;
089: import org.openide.nodes.Node;
090: import org.openide.util.Exceptions;
091: import org.openide.util.HelpCtx;
092: import org.openide.util.Lookup;
093: import org.openide.util.Mutex;
094: import org.openide.util.MutexException;
095: import org.openide.util.NbBundle;
096: import org.openide.util.Utilities;
097: import org.openide.util.actions.CookieAction;
098: import org.openide.util.actions.SystemAction;
099: import org.openide.util.lookup.Lookups;
100: import org.openide.util.lookup.ProxyLookup;
101:
102: /**
103: * @author Tomas Musil
104: */
105: final class UnitTestLibrariesNode extends AbstractNode {
106:
107: static final String UNIT_TEST_LIBRARIES_NAME = "unit libraries"; // NOI18N
108:
109: private static final String DISPLAY_NAME = getMessage("LBL_unit_test_libraries");
110:
111: private final Action[] actions;
112:
113: public UnitTestLibrariesNode(final NbModuleProject project) {
114: super (new LibrariesChildren(project));
115: setName(UNIT_TEST_LIBRARIES_NAME);
116: setDisplayName(DISPLAY_NAME);
117: actions = new Action[] { new AddUnitTestDependencyAction(
118: project) };
119: }
120:
121: public Image getIcon(int type) {
122: return getIcon(false);
123: }
124:
125: public Image getOpenedIcon(int type) {
126: return getIcon(true);
127: }
128:
129: private Image getIcon(boolean opened) {
130: Image badge = Utilities
131: .loadImage(
132: "org/netbeans/modules/apisupport/project/ui/resources/libraries-badge.png",
133: true);
134: return Utilities.mergeImages(UIUtil.getTreeFolderIcon(opened),
135: badge, 8, 8);
136: }
137:
138: public Action[] getActions(boolean context) {
139: return actions;
140: }
141:
142: private static String createHtmlDescription(
143: final TestModuleDependency dep) {
144: StringBuffer shortDesc = new StringBuffer("<html><u>"
145: + dep.getModule().getCodeNameBase() + "</u><br>"); // NOI18N
146: if (dep.isTest()) {
147: shortDesc.append("<br>" + getMessage("CTL_test"));
148: }
149: if (dep.isCompile()) {
150: shortDesc.append("<br>").append(getMessage("CTL_compile"));
151: }
152: if (dep.isRecursive()) {
153: shortDesc.append("<br>")
154: .append(getMessage("CTL_recursive"));
155: }
156: shortDesc.append("</html>"); // NOI18N
157: return shortDesc.toString();
158: }
159:
160: private static String getMessage(String bundleKey) {
161: return NbBundle.getMessage(UnitTestLibrariesNode.class,
162: bundleKey);
163: }
164:
165: private static final class LibrariesChildren extends
166: Children.Keys<Object> implements AntProjectListener {
167:
168: private static final String JUNIT = "junit"; //NOI18N
169:
170: private static final String JUNIT_CNB = "org.netbeans.modules.junit";
171:
172: private static final String NBJUNIT = "nbjunit"; //NOI18N
173:
174: private static final String NBJUNIT_CNB = "org.netbeans.modules.nbjunit";
175:
176: private static final String LIBRARIES_ICON = "org/netbeans/modules/apisupport/project/ui/resources/libraries.gif"; // NOI18N
177:
178: static final Action REMOVE_DEPENDENCY_ACTION = new RemoveDependencyAction();
179:
180: private final NbModuleProject project;
181:
182: private ImageIcon librariesIcon;
183:
184: LibrariesChildren(final NbModuleProject project) {
185: this .project = project;
186: }
187:
188: protected void addNotify() {
189: super .addNotify();
190: project.getHelper().addAntProjectListener(this );
191: refreshKeys();
192: }
193:
194: protected void removeNotify() {
195: setKeys(Collections.emptySet());
196: project.getHelper().removeAntProjectListener(this );
197: super .removeNotify();
198: }
199:
200: private void refreshKeys() {
201: try {
202: ProjectManager.mutex().readAccess(
203: new Mutex.ExceptionAction<Object>() {
204: public Object run() throws Exception {
205: ProjectXMLManager pxm = new ProjectXMLManager(
206: project);
207: List<Object> keys = new ArrayList<Object>();
208: if (isModuleInModuleList(JUNIT_CNB)) {
209: keys.add(JUNIT);
210: }
211: if (isModuleInModuleList(NBJUNIT_CNB)) {
212: keys.add(NBJUNIT);
213: }
214: SortedSet<TestModuleDependency> deps = new TreeSet<TestModuleDependency>(
215: TestModuleDependency.CNB_COMPARATOR);
216: Set<TestModuleDependency> d = pxm
217: .getTestDependencies(
218: project.getModuleList())
219: .get(TestModuleDependency.UNIT);
220: //draw only compile time deps
221: if (d != null) {
222: for (TestModuleDependency tmd : d) {
223: if (tmd.isCompile()) {
224: deps.add(tmd);
225: }
226: }
227: keys.addAll(deps);
228: }
229: setKeys(Collections
230: .unmodifiableList(keys));
231: return null;
232: }
233: });
234: } catch (MutexException e) {
235: assert false : e.getException();
236: }
237: }
238:
239: private boolean isModuleInModuleList(String cnb) {
240: ModuleEntry me = null;
241: boolean result = false;
242: try {
243: me = project.getModuleList().getEntry(cnb);
244: if (me != null) {
245: File moduleJar = me.getJarLocation();
246: result = moduleJar.exists();
247:
248: }
249: } catch (IOException ex) {
250: Exceptions.printStackTrace(ex);
251: }
252: return result;
253: }
254:
255: protected Node[] createNodes(Object key) {
256: Node node = null;
257: //special nodes - junit, nbjunit
258: if (JUNIT.equals(key) || NBJUNIT.equals(key)) {
259: String cnb = null;
260: if (JUNIT.equals(key)) {
261: cnb = JUNIT_CNB;
262: } else {
263: cnb = NBJUNIT_CNB;
264: }
265: try {
266: ModuleEntry me = project.getModuleList().getEntry(
267: cnb);
268: Icon icon = getLibrariesIcon(); // TODO a better icon for JUNIT
269: File junitJar = me.getJarLocation();
270: URL junitURL = Util.urlForJar(junitJar);
271: assert junitURL != null;
272: FileObject junitFO = URLMapper
273: .findFileObject(junitURL);
274: String name = me.getLocalizedName();
275: node = ActionFilterNode
276: .create(PackageView
277: .createPackageView(new LibrariesSourceGroup(
278: junitFO, name, icon, icon)));
279: node.setName(name); //node does not have a name by default
280: } catch (IOException ex) {
281: ex.printStackTrace();
282: }
283: } else {
284: TestModuleDependency dep = (TestModuleDependency) key;
285: File srcF = dep.getModule().getSourceLocation();
286: if (srcF == null) {
287: File jarF = dep.getModule().getJarLocation();
288: URL jarRootURL = Util.urlForJar(jarF);
289: assert jarRootURL != null;
290: FileObject root = URLMapper
291: .findFileObject(jarRootURL);
292: ModuleEntry me = dep.getModule();
293: String name = me.getLocalizedName() + " - "
294: + me.getCodeNameBase(); // NOI18N
295: Icon icon = getLibrariesIcon();
296: Node pvNode = ActionFilterNode
297: .create(PackageView
298: .createPackageView(new LibrariesSourceGroup(
299: root, name, icon, icon)));
300: node = new LibraryDependencyNode(dep, project,
301: pvNode);
302: node.setName(me.getLocalizedName());
303: } else {
304: node = new ProjectDependencyNode(dep, project);
305: node.setName(dep.getModule().getLocalizedName());
306: }
307:
308: }
309:
310: assert node != null;
311: return new Node[] { node };
312: }
313:
314: public void configurationXmlChanged(AntProjectEvent ev) {
315: // XXX this is a little strange but happens during project move. Bad ordering.
316: // Probably bug in moving implementation (our or in general Project API).
317: if (project.getHelper().resolveFileObject(
318: AntProjectHelper.PROJECT_XML_PATH) != null) {
319: refreshKeys();
320: }
321: }
322:
323: public void propertiesChanged(AntProjectEvent ev) {
324: // do not need
325: }
326:
327: private Icon getLibrariesIcon() {
328: if (librariesIcon == null) {
329: librariesIcon = new ImageIcon(Utilities.loadImage(
330: LIBRARIES_ICON, true));
331: }
332: return librariesIcon;
333: }
334:
335: }
336:
337: private static final class ProjectDependencyNode extends
338: AbstractNode {
339:
340: private final TestModuleDependency dep;
341: private final NbModuleProject project;
342: private Action[] actions;
343:
344: ProjectDependencyNode(final TestModuleDependency dep,
345: final NbModuleProject project) {
346: super (Children.LEAF, Lookups.fixed(new Object[] { dep,
347: project, dep.getModule() }));
348: this .dep = dep;
349: this .project = project;
350: ModuleEntry me = dep.getModule();
351: setIconBaseWithExtension(NbModuleProject.NB_PROJECT_ICON_PATH);
352: setDisplayName(me.getLocalizedName());
353: setShortDescription(UnitTestLibrariesNode
354: .createHtmlDescription(dep));
355: }
356:
357: public Action[] getActions(boolean context) {
358:
359: if (actions == null) {
360: Set<Action> result = new LinkedHashSet<Action>();
361: // Open project action
362: result.add(SystemAction
363: .get(LibrariesNode.OpenProjectAction.class));
364: // Edit dependency action
365: result.add(new EditTestDependencyAction(dep, project));
366: // Remove dependency
367: result.add(LibrariesChildren.REMOVE_DEPENDENCY_ACTION);
368: actions = result.toArray(new Action[result.size()]);
369: }
370: return actions;
371: }
372:
373: public Action getPreferredAction() {
374: return getActions(false)[0]; // open
375: }
376:
377: }
378:
379: private static final class LibraryDependencyNode extends FilterNode {
380:
381: private final TestModuleDependency dep;
382: private final NbModuleProject project;
383: private Action[] actions;
384:
385: LibraryDependencyNode(final TestModuleDependency dep,
386: final NbModuleProject project, final Node original) {
387: super (original, null, new ProxyLookup(new Lookup[] {
388: original.getLookup(),
389: Lookups.fixed(new Object[] { dep, project }) }));
390: this .dep = dep;
391: this .project = project;
392: setShortDescription(UnitTestLibrariesNode
393: .createHtmlDescription(dep));
394: }
395:
396: public Action[] getActions(boolean context) {
397: if (actions == null) {
398: Set<Action> result = new LinkedHashSet<Action>();
399: result.add(new EditTestDependencyAction(dep, project));
400: Action[] super Actions = super .getActions(false);
401: for (int i = 0; i < super Actions.length; i++) {
402: if (super Actions[i] instanceof FindAction) {
403: result.add(super Actions[i]);
404: }
405: }
406: result.add(LibrariesChildren.REMOVE_DEPENDENCY_ACTION);
407: actions = result.toArray(new Action[result.size()]);
408: }
409: return actions;
410: }
411:
412: public Action getPreferredAction() {
413: return getActions(false)[0];
414: }
415:
416: }
417:
418: static final class AddUnitTestDependencyAction extends
419: AbstractAction {
420:
421: private final NbModuleProject project;
422:
423: AddUnitTestDependencyAction(final NbModuleProject project) {
424: super (getMessage("CTL_AddTestDependency"));
425: this .project = project;
426: }
427:
428: //COPIED FROM LIBRARIES MOSTLY
429: public void actionPerformed(ActionEvent ev) {
430: SingleModuleProperties props = SingleModuleProperties
431: .getInstance(project);
432: final AddModulePanel addPanel = new AddModulePanel(props);
433: final DialogDescriptor descriptor = new DialogDescriptor(
434: addPanel, getMessage("CTL_AddTestDependency"));
435: descriptor.setHelpCtx(new HelpCtx(AddModulePanel.class));
436: descriptor.setClosingOptions(new Object[0]);
437: final Dialog d = DialogDisplayer.getDefault().createDialog(
438: descriptor);
439: descriptor.setButtonListener(new ActionListener() {
440: public void actionPerformed(ActionEvent e) {
441: if (DialogDescriptor.OK_OPTION
442: .equals(e.getSource())
443: && addPanel.getSelectedDependencies().length == 0) {
444: return;
445: }
446: d.setVisible(false);
447: d.dispose();
448: }
449: });
450: d.setVisible(true);
451: if (descriptor.getValue()
452: .equals(DialogDescriptor.OK_OPTION)) {
453: // dialog returns
454: ModuleDependency[] newDeps = addPanel
455: .getSelectedDependencies();
456: ProjectXMLManager pxm = new ProjectXMLManager(project);
457: try {
458: for (int i = 0; i < newDeps.length; i++) {
459: // by default, add compile-time dependency
460: pxm.addTestDependency(
461: TestModuleDependency.UNIT,
462: new TestModuleDependency(newDeps[i]
463: .getModuleEntry(), false,
464: false, true));
465: ProjectManager.getDefault()
466: .saveProject(project);
467: }
468: } catch (Exception e) {
469: //IOEX
470: ErrorManager.getDefault().annotate(
471: e,
472: "Cannot add dependencies, probably IO error: "
473: + Arrays.asList(newDeps)); // NOI18N
474: ErrorManager.getDefault().notify(e);
475: }
476: }
477: d.dispose();
478: }
479:
480: }
481:
482: static final class RemoveDependencyAction extends CookieAction {
483:
484: protected void performAction(Node[] activatedNodes) {
485: Map<NbModuleProject, Set<TestModuleDependency>> map = new HashMap<NbModuleProject, Set<TestModuleDependency>>();
486: for (int i = 0; i < activatedNodes.length; i++) {
487: TestModuleDependency dep = activatedNodes[i]
488: .getLookup().lookup(TestModuleDependency.class);
489: assert dep != null;
490: NbModuleProject project = activatedNodes[i].getLookup()
491: .lookup(NbModuleProject.class);
492: assert project != null;
493: Set<TestModuleDependency> deps = map.get(project);
494: if (deps == null) {
495: deps = new HashSet<TestModuleDependency>();
496: map.put(project, deps);
497: }
498: deps.add(dep);
499: }
500: for (Map.Entry<NbModuleProject, Set<TestModuleDependency>> me : map
501: .entrySet()) {
502: NbModuleProject project = me.getKey();
503: ProjectXMLManager pxm = new ProjectXMLManager(project);
504: //remove dep one by one
505: for (TestModuleDependency rem : me.getValue()) {
506: pxm.removeTestDependency(TestModuleDependency.UNIT,
507: rem.getModule().getCodeNameBase());
508: }
509: try {
510: ProjectManager.getDefault().saveProject(project);
511: } catch (IOException e) {
512: ErrorManager
513: .getDefault()
514: .annotate(e,
515: "Problem during test dependencies removing"); // NOI18N
516: ErrorManager.getDefault().notify(e);
517: }
518: }
519:
520: }
521:
522: public String getName() {
523: return getMessage("CTL_RemoveDependency");
524: }
525:
526: public HelpCtx getHelpCtx() {
527: return HelpCtx.DEFAULT_HELP;
528: }
529:
530: protected boolean asynchronous() {
531: return false;
532: }
533:
534: protected int mode() {
535: return CookieAction.MODE_ALL;
536: }
537:
538: protected Class[] cookieClasses() {
539: return new Class[] { TestModuleDependency.class,
540: NbModuleProject.class };
541: }
542:
543: }
544:
545: static final class EditTestDependencyAction extends AbstractAction {
546:
547: private final TestModuleDependency testDep;
548: private final NbModuleProject project;
549:
550: EditTestDependencyAction(final TestModuleDependency testDep,
551: final NbModuleProject project) {
552: super (getMessage("CTL_EditDependency"));
553: this .testDep = testDep;
554: this .project = project;
555: }
556:
557: public void actionPerformed(ActionEvent ev) {
558: final EditTestDependencyPanel editTestPanel = new EditTestDependencyPanel(
559: testDep);
560: DialogDescriptor descriptor = new DialogDescriptor(
561: editTestPanel, NbBundle.getMessage(
562: LibrariesNode.class,
563: "CTL_EditModuleDependencyTitle", testDep
564: .getModule().getLocalizedName()));
565: descriptor.setHelpCtx(new HelpCtx(
566: EditTestDependencyPanel.class));
567: Dialog d = DialogDisplayer.getDefault().createDialog(
568: descriptor);
569: d.setVisible(true);
570: if (descriptor.getValue()
571: .equals(DialogDescriptor.OK_OPTION)) {
572: TestModuleDependency editedDep = editTestPanel
573: .getEditedDependency();
574: try {
575: ProjectXMLManager pxm = new ProjectXMLManager(
576: project);
577: final String UNIT = TestModuleDependency.UNIT;
578: pxm.removeTestDependency(UNIT, testDep.getModule()
579: .getCodeNameBase());
580: pxm.addTestDependency(UNIT, editedDep);
581: ProjectManager.getDefault().saveProject(project);
582:
583: } catch (IOException e) {
584: ErrorManager.getDefault().annotate(e,
585: "Cannot store dependency: " + editedDep); // NOI18N
586: ErrorManager.getDefault().notify(e);
587: }
588:
589: }
590: d.dispose();
591: }
592: }
593:
594: }
|