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.java.j2seproject.ui;
043:
044: import java.awt.Image;
045: import java.awt.event.ActionEvent;
046: import java.beans.BeanInfo;
047: import java.beans.PropertyChangeEvent;
048: import java.beans.PropertyChangeListener;
049: import java.io.IOException;
050: import java.io.File;
051: import java.net.MalformedURLException;
052: import java.net.URI;
053: import java.net.URL;
054: import java.text.MessageFormat;
055: import java.util.ArrayList;
056: import java.util.Arrays;
057: import java.util.Collection;
058: import java.util.Collections;
059: import java.util.HashSet;
060: import java.util.Iterator;
061: import java.util.List;
062: import java.util.Set;
063: import javax.swing.AbstractAction;
064: import javax.swing.Action;
065: import javax.swing.Icon;
066: import javax.swing.ImageIcon;
067: import javax.swing.JFileChooser;
068: import javax.swing.UIManager;
069: import javax.swing.filechooser.FileFilter;
070: import org.openide.ErrorManager;
071: import org.openide.filesystems.FileObject;
072: import org.openide.filesystems.FileUtil;
073: import org.openide.filesystems.Repository;
074: import org.openide.filesystems.URLMapper;
075: import org.openide.loaders.DataFolder;
076: import org.openide.nodes.AbstractNode;
077: import org.openide.nodes.Children;
078: import org.openide.nodes.Node;
079: import org.openide.util.NbBundle;
080: import org.openide.util.RequestProcessor;
081: import org.openide.util.Utilities;
082: import org.openide.util.lookup.Lookups;
083: import org.openide.windows.WindowManager;
084: import org.netbeans.api.project.SourceGroup;
085: import org.netbeans.api.project.Project;
086: import org.netbeans.api.project.ant.AntArtifact;
087: import org.netbeans.api.project.libraries.Library;
088: import org.netbeans.api.java.classpath.ClassPath;
089: import org.netbeans.api.java.project.JavaProjectConstants;
090: import org.netbeans.api.java.project.classpath.ProjectClassPathModifier;
091: import org.netbeans.api.project.ant.FileChooser;
092: import org.netbeans.api.project.libraries.LibraryChooser;
093: import org.netbeans.api.project.libraries.LibraryManager;
094: import org.netbeans.modules.java.api.common.SourceRoots;
095: import org.netbeans.modules.java.api.common.ant.UpdateHelper;
096: import org.netbeans.modules.java.j2seproject.J2SEProject;
097: import org.netbeans.modules.java.j2seproject.classpath.ClassPathProviderImpl;
098: import org.netbeans.modules.java.j2seproject.classpath.J2SEProjectClassPathModifier;
099: import org.netbeans.spi.project.support.ant.AntProjectHelper;
100: import org.netbeans.spi.project.support.ant.EditableProperties;
101: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
102: import org.netbeans.spi.project.support.ant.PropertyUtils;
103: import org.netbeans.spi.project.support.ant.ReferenceHelper;
104: import org.netbeans.spi.java.project.support.ui.PackageView;
105: import org.netbeans.spi.java.classpath.support.ClassPathSupport;
106: import org.netbeans.modules.java.j2seproject.ui.customizer.AntArtifactChooser;
107: import org.netbeans.spi.project.libraries.support.LibrariesSupport;
108: import org.openide.util.Exceptions;
109:
110: /**
111: * LibrariesNode displays the content of classpath and optionaly Java platform.
112: * @author Tomas Zezula
113: */
114: final class LibrariesNode extends AbstractNode {
115:
116: private static final String ICON_KEY_UIMANAGER = "Tree.closedIcon"; // NOI18N
117: private static final String OPENED_ICON_KEY_UIMANAGER = "Tree.openIcon"; // NOI18N
118: private static final String ICON_KEY_UIMANAGER_NB = "Nb.Explorer.Folder.icon"; // NOI18N
119: private static final String OPENED_ICON_KEY_UIMANAGER_NB = "Nb.Explorer.Folder.openedIcon"; // NOI18N
120:
121: private static final Image ICON_BADGE = Utilities
122: .loadImage("org/netbeans/modules/java/j2seproject/ui/resources/libraries-badge.png"); //NOI18N
123: static final RequestProcessor rp = new RequestProcessor();
124: private static Image folderIconCache;
125: private static Image openedFolderIconCache;
126:
127: private final String displayName;
128: private final Action[] librariesNodeActions;
129:
130: /**
131: * Creates new LibrariesNode named displayName displaying classPathProperty classpath
132: * and optionaly Java platform.
133: * @param displayName the display name of the node
134: * @param project the project owning this node, the proejct is placed into LibrariesNode's lookup
135: * @param eval {@link PropertyEvaluator} used for listening
136: * @param helper {@link UpdateHelper} used for reading and updating project's metadata
137: * @param refHelper {@link ReferenceHelper} used for destroying unused references
138: * @param classPathProperty the ant property name of classpath which should be visualized
139: * @param classPathIgnoreRef the array of ant property names which should not be displayed, may be
140: * an empty array but not null
141: * @param platformProperty the ant name property holding the J2SE platform system name or null
142: * if the platform should not be displayed
143: * @param librariesNodeActions actions which should be available on the created node.
144: */
145: LibrariesNode(String displayName, Project project,
146: PropertyEvaluator eval, UpdateHelper helper,
147: ReferenceHelper refHelper, String classPathProperty,
148: String[] classPathIgnoreRef, String platformProperty,
149: Action[] librariesNodeActions) {
150: super (
151: new LibrariesChildren(project, eval, helper, refHelper,
152: classPathProperty, classPathIgnoreRef,
153: platformProperty), Lookups.singleton(project));
154: this .displayName = displayName;
155: this .librariesNodeActions = librariesNodeActions;
156: }
157:
158: public String getDisplayName() {
159: return this .displayName;
160: }
161:
162: public String getName() {
163: return this .getDisplayName();
164: }
165:
166: public Image getIcon(int type) {
167: return computeIcon(false, type);
168: }
169:
170: public Image getOpenedIcon(int type) {
171: return computeIcon(true, type);
172: }
173:
174: public Action[] getActions(boolean context) {
175: return this .librariesNodeActions;
176: }
177:
178: public boolean canCopy() {
179: return false;
180: }
181:
182: //Static Action Factory Methods
183: public static Action createAddProjectAction(J2SEProject p,
184: boolean sources) {
185: if (sources) {
186: return new AddProjectAction(p, p.getSourceRoots());
187: } else {
188: return new AddProjectAction(p, p.getTestSourceRoots());
189: }
190: }
191:
192: public static Action createAddLibraryAction(J2SEProject p,
193: boolean sources) {
194: if (sources) {
195: return new AddLibraryAction(p, p.getSourceRoots());
196: } else {
197: return new AddLibraryAction(p, p.getTestSourceRoots());
198: }
199: }
200:
201: public static Action createAddFolderAction(J2SEProject p,
202: boolean sources) {
203: if (sources) {
204: return new AddFolderAction(p, p.getSourceRoots());
205: } else {
206: return new AddFolderAction(p, p.getTestSourceRoots());
207: }
208: }
209:
210: /**
211: * Returns Icon of folder on active platform
212: * @param opened should the icon represent opened folder
213: * @return the folder icon
214: */
215: static synchronized Image getFolderIcon(boolean opened) {
216: if (opened) {
217: if (openedFolderIconCache == null) {
218: openedFolderIconCache = getTreeFolderIcon(opened);
219: }
220: return openedFolderIconCache;
221: } else {
222: if (folderIconCache == null) {
223: folderIconCache = getTreeFolderIcon(opened);
224: }
225: return folderIconCache;
226: }
227: }
228:
229: private Image computeIcon(boolean opened, int type) {
230: Image image = getFolderIcon(opened);
231: image = Utilities.mergeImages(image, ICON_BADGE, 7, 7);
232: return image;
233: }
234:
235: //Static inner classes
236: private static class LibrariesChildren extends Children.Keys
237: implements PropertyChangeListener {
238:
239: /**
240: * Constant represneting a prefix of library reference generated by {@link org.netbeans.modules.java.j2seplatform.libraries.J2SELibraryTypeProvider}
241: */
242: private static final String LIBRARY_PREFIX = "${libs."; // NOI18N
243:
244: /**
245: * Constant representing a prefix of artifact reference generated by {@link ReferenceHelper}
246: */
247: private static final String ANT_ARTIFACT_PREFIX = "${reference."; // NOI18N
248: /**
249: * Constant representing a prefix of file reference generated by {@link ReferenceHelper}
250: */
251: private static final String FILE_REF_PREFIX = "${file.reference."; //NOI18N
252: /**
253: * Constant representing a prefix of ant property reference
254: */
255: private static final String REF_PREFIX = "${"; //NOI18N
256:
257: private static final String LIBRARIES_ICON = "org/netbeans/modules/java/j2seproject/ui/resources/libraries.gif"; //NOI18N
258: private static final String ARCHIVE_ICON = "org/netbeans/modules/java/j2seproject/ui/resources/jar.gif";//NOI18N
259:
260: private final Project project;
261: private final PropertyEvaluator eval;
262: private final UpdateHelper helper;
263: private final ReferenceHelper refHelper;
264: private final String classPathProperty;
265: private final String platformProperty;
266: private final Set classPathIgnoreRef;
267:
268: //XXX: Workaround: classpath is used only to listen on non existent files.
269: // This should be removed when there will be API for it
270: // See issue: http://www.netbeans.org/issues/show_bug.cgi?id=33162
271: private ClassPath fsListener;
272:
273: LibrariesChildren(Project project, PropertyEvaluator eval,
274: UpdateHelper helper, ReferenceHelper refHelper,
275: String classPathProperty, String[] classPathIgnoreRef,
276: String platformProperty) {
277: this .project = project;
278: this .eval = eval;
279: this .helper = helper;
280: this .refHelper = refHelper;
281: this .classPathProperty = classPathProperty;
282: this .classPathIgnoreRef = new HashSet(Arrays
283: .asList(classPathIgnoreRef));
284: this .platformProperty = platformProperty;
285: }
286:
287: public void propertyChange(PropertyChangeEvent evt) {
288: String propName = evt.getPropertyName();
289: final boolean propRoots = ClassPath.PROP_ROOTS
290: .equals(propName);
291: if (classPathProperty.equals(propName) || propRoots
292: || LibraryManager.PROP_LIBRARIES.equals(propName)) {
293: synchronized (this ) {
294: if (fsListener != null) {
295: fsListener.removePropertyChangeListener(this );
296: }
297: }
298: rp.post(new Runnable() {
299: public void run() {
300: setKeys(getKeys());
301: if (propRoots) {
302: J2SELogicalViewProvider lvp = project
303: .getLookup()
304: .lookup(
305: J2SELogicalViewProvider.class);
306: if (lvp != null) {
307: lvp.testBroken();
308: }
309: }
310: }
311: });
312: }
313: }
314:
315: protected void addNotify() {
316: this .eval.addPropertyChangeListener(this );
317: if (refHelper.getProjectLibraryManager() != null) {
318: refHelper.getProjectLibraryManager()
319: .addPropertyChangeListener(this );
320: } else {
321: LibraryManager.getDefault().addPropertyChangeListener(
322: this );
323: }
324: this .setKeys(getKeys());
325: }
326:
327: protected void removeNotify() {
328: this .eval.removePropertyChangeListener(this );
329: if (refHelper.getProjectLibraryManager() != null) {
330: refHelper.getProjectLibraryManager()
331: .removePropertyChangeListener(this );
332: } else {
333: LibraryManager.getDefault()
334: .removePropertyChangeListener(this );
335: }
336: synchronized (this ) {
337: if (fsListener != null) {
338: fsListener.removePropertyChangeListener(this );
339: fsListener = null;
340: }
341: }
342: this .setKeys(Collections.EMPTY_SET);
343: }
344:
345: protected Node[] createNodes(Object obj) {
346: Node[] result = null;
347: if (obj instanceof Key) {
348: Key key = (Key) obj;
349: switch (key.getType()) {
350: case Key.TYPE_PLATFORM:
351: result = new Node[] { PlatformNode.create(eval,
352: platformProperty) };
353: break;
354: case Key.TYPE_PROJECT:
355: result = new Node[] { new ProjectNode(key
356: .getProject(), key.getArtifactLocation(),
357: helper, refHelper, key.getClassPathId(),
358: key.getEntryId()) };
359: break;
360: case Key.TYPE_LIBRARY:
361: result = new Node[] { ActionFilterNode.create(
362: PackageView.createPackageView(key
363: .getSourceGroup()), helper,
364: refHelper, key.getClassPathId(), key
365: .getEntryId()) };
366: break;
367: }
368: }
369: if (result == null) {
370: assert false : "Unknown key type"; //NOI18N
371: result = new Node[0];
372: }
373: return result;
374: }
375:
376: private List getKeys() {
377: EditableProperties projectSharedProps = helper
378: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
379: EditableProperties projectPrivateProps = helper
380: .getProperties(AntProjectHelper.PRIVATE_PROPERTIES_PATH);
381: EditableProperties privateProps = PropertyUtils
382: .getGlobalProperties();
383: List/*<URL>*/rootsList = new ArrayList();
384: List result = getKeys(projectSharedProps,
385: projectPrivateProps, privateProps,
386: classPathProperty, rootsList);
387: //Add PlatformNode if needed and project exists
388: FileObject projectDir = helper.getAntProjectHelper()
389: .getProjectDirectory();
390: if (platformProperty != null && projectDir != null
391: && projectDir.isValid() && !projectDir.isVirtual()) {
392: result.add(new Key());
393: }
394: //XXX: Workaround: Remove this when there will be API for listening on nonexistent files
395: // See issue: http://www.netbeans.org/issues/show_bug.cgi?id=33162
396: ClassPath cp = ClassPathSupport
397: .createClassPath((URL[]) rootsList
398: .toArray(new URL[rootsList.size()]));
399: cp.addPropertyChangeListener(this );
400: cp.getRoots();
401: synchronized (this ) {
402: fsListener = cp;
403: }
404: return result;
405: }
406:
407: private List getKeys(EditableProperties projectSharedProps,
408: EditableProperties projectPrivateProps,
409: EditableProperties privateProps,
410: String currentClassPath, List/*<URL>*/rootsList) {
411: List result = new ArrayList();
412: String raw = projectSharedProps
413: .getProperty(currentClassPath);
414: if (raw == null) {
415: raw = projectPrivateProps.getProperty(currentClassPath);
416: }
417: if (raw == null) {
418: raw = privateProps.getProperty(currentClassPath);
419: }
420: if (raw == null) {
421: return result;
422: }
423: List pe = new ArrayList(Arrays.asList(PropertyUtils
424: .tokenizePath(raw)));
425: while (pe.size() > 0) {
426: String prop = (String) pe.remove(0);
427: String propName = org.netbeans.modules.java.j2seproject.classpath.ClassPathSupport
428: .getAntPropertyName(prop);
429: if (classPathIgnoreRef.contains(propName)) {
430: continue;
431: } else if (prop.startsWith(LIBRARY_PREFIX)) {
432: //Library reference
433: String val = prop.substring(
434: LIBRARY_PREFIX.length(), prop
435: .lastIndexOf('.')); //NOI18N
436: Library lib = refHelper.findLibrary(val);
437: if (lib != null) {
438: List/*<URL>*/roots = lib
439: .getContent("classpath"); //NOI18N
440: Icon libIcon = new ImageIcon(Utilities
441: .loadImage(LIBRARIES_ICON));
442: for (Iterator it = roots.iterator(); it
443: .hasNext();) {
444: URL rootUrl = (URL) it.next();
445: rootsList.add(rootUrl);
446: FileObject root = URLMapper
447: .findFileObject(rootUrl);
448: if (root != null) {
449: String displayName;
450: if ("jar".equals(rootUrl.getProtocol())) { //NOI18N
451: FileObject file = FileUtil
452: .getArchiveFile(root);
453: displayName = file.getNameExt();
454: } else {
455: File file = FileUtil.toFile(root);
456: if (file != null) {
457: displayName = file
458: .getAbsolutePath();
459: } else {
460: displayName = root.getNameExt();
461: }
462: }
463: displayName = MessageFormat
464: .format(
465: NbBundle
466: .getMessage(
467: LibrariesNode.class,
468: "TXT_LibraryPartFormat"),
469: new Object[] {
470: lib
471: .getDisplayName(),
472: displayName });
473: SourceGroup sg = new LibrariesSourceGroup(
474: root, displayName, libIcon,
475: libIcon);
476: result.add(new Key(sg,
477: currentClassPath, propName));
478: }
479: }
480: }
481: //Todo: May try to resolve even broken library
482: } else if (prop.startsWith(ANT_ARTIFACT_PREFIX)) {
483: //Project reference
484: Object ret[] = refHelper
485: .findArtifactAndLocation(prop);
486: if (ret[0] != null && ret[1] != null) {
487: AntArtifact artifact = (AntArtifact) ret[0];
488: URI uri = (URI) ret[1];
489: result.add(new Key(artifact, uri,
490: currentClassPath, propName));
491: }
492: } else if (prop.startsWith(FILE_REF_PREFIX)) {
493: //File reference
494: String evaluatedRef = eval.getProperty(propName);
495: if (evaluatedRef != null) {
496: File file = helper.getAntProjectHelper()
497: .resolveFile(evaluatedRef);
498: SourceGroup sg = createFileSourceGroup(file,
499: rootsList);
500: if (sg != null) {
501: result.add(new Key(sg, currentClassPath,
502: propName));
503: }
504: }
505: } else if (prop.startsWith(REF_PREFIX)) {
506: //Path reference
507: result.addAll(getKeys(projectSharedProps,
508: projectPrivateProps, privateProps,
509: propName, rootsList));
510: } else {
511: //file
512: File file = helper.getAntProjectHelper()
513: .resolveFile(prop);
514: SourceGroup sg = createFileSourceGroup(file,
515: rootsList);
516: if (sg != null) {
517: result.add(new Key(sg, currentClassPath,
518: propName));
519: }
520: }
521: }
522: return result;
523: }
524:
525: private static SourceGroup createFileSourceGroup(File file,
526: List/*<URL>*/rootsList) {
527: Icon icon;
528: Icon openedIcon;
529: String displayName;
530: try {
531: URL url = file.toURI().toURL();
532: if (FileUtil.isArchiveFile(url)) {
533: url = FileUtil.getArchiveRoot(url);
534: icon = openedIcon = new ImageIcon(Utilities
535: .loadImage(ARCHIVE_ICON));
536: displayName = file.getName();
537: } else {
538: String sURL = url.toExternalForm();
539: if (!sURL.endsWith("/")) { //NOI18N
540: url = new URL(sURL + "/"); //NOI18N
541: }
542: icon = new ImageIcon(getFolderIcon(false));
543: openedIcon = new ImageIcon(getFolderIcon(true));
544: displayName = file.getAbsolutePath();
545: }
546: rootsList.add(url);
547: FileObject root = URLMapper.findFileObject(url);
548: if (root != null) {
549: return new LibrariesSourceGroup(root, displayName,
550: icon, openedIcon);
551: }
552: } catch (MalformedURLException e) {
553: ErrorManager.getDefault().notify(e);
554: }
555: return null;
556: }
557: }
558:
559: private static class Key {
560: static final int TYPE_PLATFORM = 0;
561: static final int TYPE_LIBRARY = 1;
562: static final int TYPE_PROJECT = 2;
563:
564: private int type;
565: private String classPathId;
566: private String entryId;
567: private SourceGroup sg;
568: private AntArtifact antArtifact;
569: private URI uri;
570:
571: Key() {
572: this .type = TYPE_PLATFORM;
573: }
574:
575: Key(SourceGroup sg, String classPathId, String entryId) {
576: this .type = TYPE_LIBRARY;
577: this .sg = sg;
578: this .classPathId = classPathId;
579: this .entryId = entryId;
580: }
581:
582: Key(AntArtifact a, URI uri, String classPathId, String entryId) {
583: this .type = TYPE_PROJECT;
584: this .antArtifact = a;
585: this .uri = uri;
586: this .classPathId = classPathId;
587: this .entryId = entryId;
588: }
589:
590: public int getType() {
591: return this .type;
592: }
593:
594: public String getClassPathId() {
595: return this .classPathId;
596: }
597:
598: public String getEntryId() {
599: return this .entryId;
600: }
601:
602: public SourceGroup getSourceGroup() {
603: return this .sg;
604: }
605:
606: public AntArtifact getProject() {
607: return this .antArtifact;
608: }
609:
610: public URI getArtifactLocation() {
611: return this .uri;
612: }
613:
614: public int hashCode() {
615: int hashCode = this .type << 16;
616: switch (this .type) {
617: case TYPE_LIBRARY:
618: hashCode ^= this .sg == null ? 0 : this .sg.hashCode();
619: break;
620: case TYPE_PROJECT:
621: hashCode ^= this .antArtifact == null ? 0
622: : this .antArtifact.hashCode();
623: break;
624: }
625: return hashCode;
626: }
627:
628: public boolean equals(Object obj) {
629: if (!(obj instanceof Key)) {
630: return false;
631: }
632: Key other = (Key) obj;
633: if (other.type != type) {
634: return false;
635: }
636: switch (type) {
637: case TYPE_LIBRARY:
638: return (this .sg == null ? other.sg == null : this .sg
639: .equals(other.sg))
640: && (this .classPathId == null ? other.classPathId == null
641: : this .classPathId
642: .equals(other.classPathId))
643: && (this .entryId == null ? other.entryId == null
644: : this .entryId.equals(other.entryId));
645: case TYPE_PROJECT:
646: return (this .antArtifact == null ? other.antArtifact == null
647: : this .antArtifact.equals(other.antArtifact))
648: && (this .classPathId == null ? other.classPathId == null
649: : this .classPathId
650: .equals(other.classPathId))
651: && (this .entryId == null ? other.entryId == null
652: : this .entryId.equals(other.entryId));
653: case TYPE_PLATFORM:
654: return true;
655: default:
656: throw new IllegalStateException();
657: }
658: }
659: }
660:
661: private static class AddProjectAction extends AbstractAction {
662:
663: private final J2SEProject project;
664: private final SourceRoots projectSourcesArtifact;
665:
666: public AddProjectAction(final J2SEProject project,
667: final SourceRoots projectSourcesArtifact) {
668: super (NbBundle.getMessage(LibrariesNode.class,
669: "LBL_AddProject_Action"));
670: assert project != null;
671: assert projectSourcesArtifact != null;
672: this .project = project;
673: this .projectSourcesArtifact = projectSourcesArtifact;
674: }
675:
676: public void actionPerformed(ActionEvent e) {
677: final ClassPathProviderImpl cpProvider = project
678: .getClassPathProvider();
679: assert cpProvider != null;
680: final J2SEProjectClassPathModifier cpMod = this .project
681: .getLookup().lookup(
682: J2SEProjectClassPathModifier.class);
683: assert cpMod != null;
684: AntArtifactChooser.ArtifactItem ai[] = AntArtifactChooser
685: .showDialog(
686: new String[] {
687: JavaProjectConstants.ARTIFACT_TYPE_JAR,
688: JavaProjectConstants.ARTIFACT_TYPE_FOLDER },
689: project, null);
690: if (ai != null) {
691: final String propName = cpProvider.getPropertyName(
692: projectSourcesArtifact, ClassPath.COMPILE);
693: addArtifacts(ai, cpMod, propName);
694: }
695: }
696:
697: private void addArtifacts(
698: final AntArtifactChooser.ArtifactItem[] artifactItems,
699: final J2SEProjectClassPathModifier cpMod,
700: final String propName) {
701: assert artifactItems != null;
702: assert cpMod != null;
703: assert propName != null;
704: for (int i = 0; i < artifactItems.length; i++) {
705: try {
706: cpMod.handleAntArtifacts(
707: new AntArtifact[] { artifactItems[i]
708: .getArtifact() },
709: new URI[] { artifactItems[i]
710: .getArtifactURI() }, propName,
711: J2SEProjectClassPathModifier.ADD);
712: } catch (IOException ioe) {
713: ErrorManager.getDefault().notify(ioe);
714: }
715: }
716: }
717: }
718:
719: private static class AddLibraryAction extends AbstractAction {
720:
721: private final J2SEProject project;
722: private final SourceRoots projectSourcesArtifact;
723:
724: public AddLibraryAction(final J2SEProject project,
725: final SourceRoots projectSourcesArtifact) {
726: super (NbBundle.getMessage(LibrariesNode.class,
727: "LBL_AddLibrary_Action"));
728: assert project != null;
729: assert projectSourcesArtifact != null;
730: this .project = project;
731: this .projectSourcesArtifact = projectSourcesArtifact;
732: }
733:
734: public void actionPerformed(ActionEvent e) {
735: final ClassPathProviderImpl cpProvider = project
736: .getClassPathProvider();
737: assert cpProvider != null;
738: final J2SEProjectClassPathModifier cpMod = project
739: .getProjectClassPathModifier();
740: assert cpMod != null;
741: Set<Library> added = LibraryChooser.showDialog(project
742: .getReferenceHelper().getProjectLibraryManager(),
743: null, project.getReferenceHelper()
744: .getLibraryChooserImportHandler()); // XXX restrict to j2se libs only?
745: if (added != null) {
746: final String propName = cpProvider.getPropertyName(
747: projectSourcesArtifact, ClassPath.COMPILE);
748: addLibraries(added.toArray(new Library[added.size()]),
749: cpMod, propName);
750: }
751: }
752:
753: private void addLibraries(final Library[] libraries,
754: final J2SEProjectClassPathModifier cpMod,
755: final String propName) {
756: assert libraries != null;
757: assert cpMod != null;
758: assert propName != null;
759: for (int i = 0; i < libraries.length; i++) {
760: try {
761: cpMod.handleLibraries(
762: new Library[] { libraries[i] }, propName,
763: J2SEProjectClassPathModifier.ADD);
764: } catch (IOException ioe) {
765: ErrorManager.getDefault().notify(ioe);
766: }
767: }
768: }
769:
770: }
771:
772: /**
773: * Returns default folder icon as {@link java.awt.Image}. Never returns
774: * <code>null</code>.
775: *
776: * @param opened wheter closed or opened icon should be returned.
777: */
778: private static Image getTreeFolderIcon(boolean opened) {
779: Image base = null;
780: Icon baseIcon = UIManager
781: .getIcon(opened ? OPENED_ICON_KEY_UIMANAGER
782: : ICON_KEY_UIMANAGER); // #70263
783: if (baseIcon != null) {
784: base = Utilities.icon2Image(baseIcon);
785: } else {
786: base = (Image) UIManager
787: .get(opened ? OPENED_ICON_KEY_UIMANAGER_NB
788: : ICON_KEY_UIMANAGER_NB); // #70263
789: if (base == null) { // fallback to our owns
790: final Node n = DataFolder.findFolder(
791: Repository.getDefault().getDefaultFileSystem()
792: .getRoot()).getNodeDelegate();
793: base = opened ? n
794: .getOpenedIcon(BeanInfo.ICON_COLOR_16x16) : n
795: .getIcon(BeanInfo.ICON_COLOR_16x16);
796: }
797: }
798: assert base != null;
799: return base;
800: }
801:
802: private static class AddFolderAction extends AbstractAction {
803:
804: private final J2SEProject project;
805: private final SourceRoots projectSourcesArtifact;
806:
807: public AddFolderAction(final J2SEProject project,
808: final SourceRoots projectSourcesArtifact) {
809: super (NbBundle.getMessage(LibrariesNode.class,
810: "LBL_AddFolder_Action"));
811: assert project != null;
812: assert projectSourcesArtifact != null;
813: this .project = project;
814: this .projectSourcesArtifact = projectSourcesArtifact;
815: }
816:
817: public void actionPerformed(ActionEvent e) {
818: final ClassPathProviderImpl cpProvider = project
819: .getClassPathProvider();
820: assert cpProvider != null;
821: final J2SEProjectClassPathModifier cpMod = project
822: .getProjectClassPathModifier();
823: assert cpMod != null;
824: FileChooser chooser = new FileChooser(project
825: .getAntProjectHelper(), true);
826: FileUtil.preventFileChooserSymlinkTraversal(chooser, null);
827: chooser
828: .setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
829: chooser.setMultiSelectionEnabled(true);
830: chooser.setDialogTitle(NbBundle.getMessage(
831: LibrariesNode.class, "LBL_AddJar_DialogTitle")); // NOI18N
832: FileFilter fileFilter = new SimpleFileFilter(NbBundle
833: .getMessage(LibrariesNode.class,
834: "LBL_ZipJarFolderFilter"), // NOI18N
835: new String[] { "ZIP", "JAR" }); // NOI18N
836: //#61789 on old macosx (jdk 1.4.1) these two method need to be called in this order.
837: chooser.setAcceptAllFileFilterUsed(false);
838: chooser.setFileFilter(fileFilter);
839: File curDir = FoldersListSettings.getDefault()
840: .getLastUsedClassPathFolder();
841: chooser.setCurrentDirectory(curDir);
842: int option = chooser.showOpenDialog(WindowManager
843: .getDefault().getMainWindow());
844: if (option == JFileChooser.APPROVE_OPTION) {
845: String files[];
846: try {
847: files = chooser.getSelectedPaths();
848: } catch (IOException ex) {
849: Exceptions.printStackTrace(ex);
850: return;
851: }
852: final String propName = cpProvider.getPropertyName(
853: projectSourcesArtifact, ClassPath.COMPILE);
854: addJarFiles(files, fileFilter, FileUtil.toFile(project
855: .getProjectDirectory()), cpMod, propName);
856: curDir = FileUtil.normalizeFile(chooser
857: .getCurrentDirectory());
858: FoldersListSettings.getDefault()
859: .setLastUsedClassPathFolder(curDir);
860: }
861: }
862:
863: private void addJarFiles(final String[] files,
864: final FileFilter fileFilter, final File base,
865: final J2SEProjectClassPathModifier cpMod,
866: final String propName) {
867: assert files != null;
868: assert fileFilter != null;
869: assert cpMod != null;
870: assert propName != null;
871: for (int i = 0; i < files.length; i++) {
872: try {
873: //Check if the file is acceted by the FileFilter,
874: //user may enter the name of non displayed file into JFileChooser
875: File fl = PropertyUtils.resolveFile(base, files[i]);
876: if (fileFilter.accept(fl)) {
877: URL u = LibrariesSupport
878: .convertFilePathToURL(files[i]);
879: u = FileUtil.getArchiveRoot(u);
880: cpMod
881: .handleRoots(
882: new URL[] { u },
883: propName,
884: J2SEProjectClassPathModifier.ADD,
885: false);
886: }
887: } catch (IOException ioe) {
888: ErrorManager.getDefault().notify(ioe);
889: }
890: }
891: }
892:
893: }
894:
895: private static class SimpleFileFilter extends FileFilter {
896:
897: private String description;
898: private Collection extensions;
899:
900: public SimpleFileFilter(String description, String[] extensions) {
901: this .description = description;
902: this .extensions = Arrays.asList(extensions);
903: }
904:
905: public boolean accept(File f) {
906: if (f.isDirectory())
907: return true;
908: try {
909: return FileUtil.isArchiveFile(f.toURI().toURL());
910: } catch (MalformedURLException mue) {
911: ErrorManager.getDefault().notify(mue);
912: return false;
913: }
914: }
915:
916: public String getDescription() {
917: return this.description;
918: }
919: }
920: }
|