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.ruby.rubyproject;
043:
044: import java.awt.Component;
045: import java.awt.Dialog;
046: import java.awt.event.ActionEvent;
047: import java.awt.event.ActionListener;
048: import java.beans.BeanInfo;
049: import java.io.File;
050: import java.text.MessageFormat;
051: import java.util.Arrays;
052: import java.util.Collection;
053: import java.util.Collections;
054: import java.util.HashMap;
055: import java.util.HashSet;
056: import java.util.Iterator;
057: import java.util.Map;
058: import java.util.Set;
059: import java.util.regex.Matcher;
060: import java.util.regex.Pattern;
061: import javax.swing.ButtonModel;
062: import javax.swing.DefaultListCellRenderer;
063: import javax.swing.DefaultListModel;
064: import javax.swing.Icon;
065: import javax.swing.ImageIcon;
066: import javax.swing.JButton;
067: import javax.swing.JFileChooser;
068: import javax.swing.JList;
069: import javax.swing.ListCellRenderer;
070: import javax.swing.ListSelectionModel;
071: import javax.swing.SwingUtilities;
072: import javax.swing.event.ListSelectionEvent;
073: import javax.swing.event.ListSelectionListener;
074: import javax.swing.filechooser.FileFilter;
075: import org.netbeans.api.project.Project;
076: import org.netbeans.api.project.ProjectInformation;
077: import org.netbeans.api.project.ProjectUtils;
078: import org.netbeans.modules.ruby.spi.project.support.rake.PropertyEvaluator;
079: import org.openide.DialogDescriptor;
080: import org.openide.DialogDisplayer;
081: import org.openide.filesystems.FileObject;
082: import org.openide.filesystems.FileUtil;
083: import org.openide.filesystems.Repository;
084: import org.openide.loaders.DataFolder;
085: import org.openide.util.NbBundle;
086: import org.openide.util.Utilities;
087:
088: /** Classes containing code speciic for handling UI of J2SE project classpath
089: *
090: * @author Petr Hrebejk
091: */
092: public class JavaClassPathUi {
093:
094: private JavaClassPathUi() {
095: }
096:
097: // Innerclasses ------------------------------------------------------------
098:
099: /** Renderer which can be used to render the classpath in lists
100: */
101: public static class ClassPathListCellRenderer extends
102: DefaultListCellRenderer {
103:
104: private static final Pattern FOREIGN_PLAIN_FILE_REFERENCE = Pattern
105: .compile("\\$\\{file\\.reference\\.([^${}]+)\\}"); // NOI18N
106: private static final Pattern UNKNOWN_FILE_REFERENCE = Pattern
107: .compile("\\$\\{([^${}]+)\\}"); // NOI18N
108:
109: private static String RESOURCE_ICON_JAR = "org/netbeans/modules/ruby/rubyproject/ui/resources/jar.gif"; //NOI18N
110: private static String RESOURCE_ICON_LIBRARY = "org/netbeans/modules/ruby/rubyproject/ui/resources/libraries.gif"; //NOI18N
111: private static String RESOURCE_ICON_ARTIFACT = "org/netbeans/modules/ruby/rubyproject/ui/resources/projectDependencies.gif"; //NOI18N
112: private static String RESOURCE_ICON_CLASSPATH = "org/netbeans/modules/ruby/rubyproject/ui/resources/referencedClasspath.gif"; //NOI18N
113: private static String RESOURCE_ICON_BROKEN_BADGE = "org/netbeans/modules/ruby/rubyproject/ui/resources/brokenProjectBadge.gif"; //NOI18N
114:
115: private static ImageIcon ICON_JAR = new ImageIcon(Utilities
116: .loadImage(RESOURCE_ICON_JAR));
117: private static ImageIcon ICON_FOLDER = null;
118: private static ImageIcon ICON_LIBRARY = new ImageIcon(Utilities
119: .loadImage(RESOURCE_ICON_LIBRARY));
120: private static ImageIcon ICON_ARTIFACT = new ImageIcon(
121: Utilities.loadImage(RESOURCE_ICON_ARTIFACT));
122: private static ImageIcon ICON_CLASSPATH = new ImageIcon(
123: Utilities.loadImage(RESOURCE_ICON_CLASSPATH));
124: private static ImageIcon ICON_BROKEN_BADGE = new ImageIcon(
125: Utilities.loadImage(RESOURCE_ICON_BROKEN_BADGE));
126:
127: private static ImageIcon ICON_BROKEN_JAR;
128: private static ImageIcon ICON_BROKEN_LIBRARY;
129: private static ImageIcon ICON_BROKEN_ARTIFACT;
130:
131: private PropertyEvaluator evaluator;
132:
133: // Contains well known paths in the RubyProject
134: private static final Map WELL_KNOWN_PATHS_NAMES = new HashMap();
135: static {
136: // WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.JAVAC_CLASSPATH, NbBundle.getMessage( RubyProjectProperties.class, "LBL_JavacClasspath_DisplayName" ) );
137: // WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.JAVAC_TEST_CLASSPATH, NbBundle.getMessage( RubyProjectProperties.class,"LBL_JavacTestClasspath_DisplayName") );
138: // WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.RUN_CLASSPATH, NbBundle.getMessage( RubyProjectProperties.class, "LBL_RunClasspath_DisplayName" ) );
139: // WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.RUN_TEST_CLASSPATH, NbBundle.getMessage( RubyProjectProperties.class, "LBL_RunTestClasspath_DisplayName" ) );
140: // WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.BUILD_CLASSES_DIR, NbBundle.getMessage( RubyProjectProperties.class, "LBL_BuildClassesDir_DisplayName" ) );
141: // WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.BUILD_TEST_CLASSES_DIR, NbBundle.getMessage (RubyProjectProperties.class,"LBL_BuildTestClassesDir_DisplayName") );
142: };
143:
144: public ClassPathListCellRenderer(PropertyEvaluator evaluator) {
145: super ();
146: this .evaluator = evaluator;
147: }
148:
149: public Component getListCellRendererComponent(JList list,
150: Object value, int index, boolean isSelected,
151: boolean cellHasFocus) {
152:
153: ProjectPropertyExtender.Item item = (ProjectPropertyExtender.Item) value;
154:
155: super .getListCellRendererComponent(list,
156: getDisplayName(item), index, isSelected,
157: cellHasFocus);
158: setIcon(getIcon(item));
159: setToolTipText(getToolTipText(item));
160:
161: return this ;
162: }
163:
164: private String getDisplayName(ProjectPropertyExtender.Item item) {
165:
166: switch (item.getType()) {
167:
168: // case ProjectPropertyExtender.Item.TYPE_LIBRARY:
169: // if ( item.isBroken() ) {
170: // return NbBundle.getMessage( RubyClassPathUi.class, "LBL_MISSING_LIBRARY", getLibraryName( item ) );
171: // }
172: // else {
173: // return item.getLibrary().getDisplayName();
174: // }
175: // case ProjectPropertyExtender.Item.TYPE_CLASSPATH:
176: // String name = (String)WELL_KNOWN_PATHS_NAMES.get( ClassPathSupport.getAntPropertyName( item.getReference() ) );
177: // return name == null ? item.getReference() : name;
178: // case ProjectPropertyExtender.Item.TYPE_ARTIFACT:
179: // if ( item.isBroken() ) {
180: // return NbBundle.getMessage( RubyClassPathUi.class, "LBL_MISSING_PROJECT", getProjectName( item ) );
181: // }
182: // else {
183: // Project p = item.getArtifact().getProject();
184: // ProjectInformation pi = ProjectUtils.getInformation(p);
185: // String projectName = pi.getDisplayName();
186: // return MessageFormat.format (NbBundle.getMessage(RubyClassPathUi.class,"MSG_ProjectArtifactFormat"), new Object[] {
187: // projectName,
188: // item.getArtifactURI().toString()
189: // });
190: // }
191: case ProjectPropertyExtender.Item.TYPE_JAR:
192: if (item.isBroken()) {
193: return NbBundle.getMessage(JavaClassPathUi.class,
194: "LBL_MISSING_FILE", getFileRefName(item));
195: } else {
196: return item.getFile().getPath();
197: }
198: }
199:
200: return item.getReference(); // XXX
201: }
202:
203: static Icon getIcon(ProjectPropertyExtender.Item item) {
204:
205: switch (item.getType()) {
206:
207: case ProjectPropertyExtender.Item.TYPE_LIBRARY:
208: if (item.isBroken()) {
209: if (ICON_BROKEN_LIBRARY == null) {
210: ICON_BROKEN_LIBRARY = new ImageIcon(Utilities
211: .mergeImages(ICON_LIBRARY.getImage(),
212: ICON_BROKEN_BADGE.getImage(),
213: 7, 7));
214: }
215: return ICON_BROKEN_LIBRARY;
216: } else {
217: return ICON_LIBRARY;
218: }
219: // case ProjectPropertyExtender.Item.TYPE_ARTIFACT:
220: // if ( item.isBroken() ) {
221: // if ( ICON_BROKEN_ARTIFACT == null ) {
222: // ICON_BROKEN_ARTIFACT = new ImageIcon( Utilities.mergeImages( ICON_ARTIFACT.getImage(), ICON_BROKEN_BADGE.getImage(), 7, 7 ) );
223: // }
224: // return ICON_BROKEN_ARTIFACT;
225: // }
226: // else {
227: // Project p = item.getArtifact().getProject();
228: // if (p != null) {
229: // ProjectInformation pi = ProjectUtils.getInformation(p);
230: // return pi.getIcon();
231: // }
232: // return ICON_ARTIFACT;
233: // }
234: case ProjectPropertyExtender.Item.TYPE_JAR:
235: if (item.isBroken()) {
236: if (ICON_BROKEN_JAR == null) {
237: ICON_BROKEN_JAR = new ImageIcon(Utilities
238: .mergeImages(ICON_JAR.getImage(),
239: ICON_BROKEN_BADGE.getImage(),
240: 7, 7));
241: }
242: return ICON_BROKEN_JAR;
243: } else {
244: File file = item.getFile();
245: return file.isDirectory() ? getFolderIcon()
246: : ICON_JAR;
247: }
248: case ProjectPropertyExtender.Item.TYPE_CLASSPATH:
249: return ICON_CLASSPATH;
250:
251: }
252:
253: return null; // XXX
254: }
255:
256: private String getToolTipText(ProjectPropertyExtender.Item item) {
257: if (item.isBroken()
258: && (item.getType() == ProjectPropertyExtender.Item.TYPE_JAR || item
259: .getType() == ProjectPropertyExtender.Item.TYPE_ARTIFACT)) {
260: return evaluator.evaluate(item.getReference());
261: }
262:
263: return getDisplayName(item); // XXX
264: }
265:
266: private static ImageIcon getFolderIcon() {
267:
268: if (ICON_FOLDER == null) {
269: FileObject root = Repository.getDefault()
270: .getDefaultFileSystem().getRoot();
271: DataFolder dataFolder = DataFolder.findFolder(root);
272: ICON_FOLDER = new ImageIcon(dataFolder
273: .getNodeDelegate().getIcon(
274: BeanInfo.ICON_COLOR_16x16));
275: }
276:
277: return ICON_FOLDER;
278: }
279:
280: private String getProjectName(ProjectPropertyExtender.Item item) {
281: String ID = item.getReference();
282: // something in the form of "${reference.project-name.id}"
283: return ID.substring(12, ID.indexOf(".", 12)); // NOI18N
284: }
285:
286: private String getLibraryName(ProjectPropertyExtender.Item item) {
287: String ID = item.getReference();
288: // something in the form of "${libs.junit.classpath}"
289: return ID.substring(7, ID.indexOf(".classpath")); // NOI18N
290: }
291:
292: private String getFileRefName(ProjectPropertyExtender.Item item) {
293: String ID = item.getReference();
294: // something in the form of "${file.reference.smth.jar}"
295: Matcher m = FOREIGN_PLAIN_FILE_REFERENCE.matcher(ID);
296: if (m.matches()) {
297: return m.group(1);
298: }
299: m = UNKNOWN_FILE_REFERENCE.matcher(ID);
300: if (m.matches()) {
301: return m.group(1);
302: }
303: return ID;
304: }
305: }
306:
307: //
308: // public static class EditMediator implements ActionListener, ListSelectionListener {
309: //
310: // private final Project project;
311: // private final JList list;
312: // private final DefaultListModel listModel;
313: // private final ListSelectionModel selectionModel;
314: // private final ButtonModel addJar;
315: // private final ButtonModel addLibrary;
316: // private final ButtonModel addAntArtifact;
317: // private final ButtonModel remove;
318: // private final ButtonModel moveUp;
319: // private final ButtonModel moveDown;
320: //
321: // public EditMediator( Project project,
322: // JList list,
323: // DefaultListModel listModel,
324: // ButtonModel addJar,
325: // ButtonModel addLibrary,
326: // ButtonModel addAntArtifact,
327: // ButtonModel remove,
328: // ButtonModel moveUp,
329: // ButtonModel moveDown ) {
330: //
331: // // Remember all buttons
332: //
333: // this.list = list;
334: //
335: // if ( !( list.getModel() instanceof DefaultListModel ) ) {
336: // throw new IllegalArgumentException( "The list's model has to be of class DefaultListModel" ); // NOI18N
337: // }
338: //
339: // this.listModel = (DefaultListModel)list.getModel();
340: // this.selectionModel = list.getSelectionModel();
341: //
342: // this.addJar = addJar;
343: // this.addLibrary = addLibrary;
344: // this.addAntArtifact = addAntArtifact;
345: // this.remove = remove;
346: // this.moveUp = moveUp;
347: // this.moveDown = moveDown;
348: //
349: // this.project = project;
350: // }
351: //
352: // public static void register(Project project,
353: // JList list,
354: // DefaultListModel listModel,
355: // ButtonModel addJar,
356: // ButtonModel addLibrary,
357: // ButtonModel addAntArtifact,
358: // ButtonModel remove,
359: // ButtonModel moveUp,
360: // ButtonModel moveDown ) {
361: //
362: // EditMediator em = new EditMediator( project,
363: // list,
364: // listModel,
365: // addJar,
366: // addLibrary,
367: // addAntArtifact,
368: // remove,
369: // moveUp,
370: // moveDown );
371: //
372: // // Register the listener on all buttons
373: // addJar.addActionListener( em );
374: // addLibrary.addActionListener( em );
375: // addAntArtifact.addActionListener( em );
376: // remove.addActionListener( em );
377: // moveUp.addActionListener( em );
378: // moveDown.addActionListener( em );
379: // // On list selection
380: // em.selectionModel.addListSelectionListener( em );
381: // // Set the initial state of the buttons
382: // em.valueChanged( null );
383: // }
384: //
385: // // Implementation of ActionListener ------------------------------------
386: //
387: // /** Handles button events
388: // */
389: // public void actionPerformed( ActionEvent e ) {
390: //
391: // Object source = e.getSource();
392: //
393: // if ( source == addJar ) {
394: // // Let user search for the Jar file
395: // JFileChooser chooser = new JFileChooser();
396: // FileUtil.preventFileChooserSymlinkTraversal(chooser, null);
397: // chooser.setFileSelectionMode( JFileChooser.FILES_AND_DIRECTORIES );
398: // chooser.setMultiSelectionEnabled( true );
399: // chooser.setDialogTitle( NbBundle.getMessage( J2SEClassPathUi.class, "LBL_AddJar_DialogTitle" ) ); // NOI18N
400: // //#61789 on old macosx (jdk 1.4.1) these two method need to be called in this order.
401: // chooser.setAcceptAllFileFilterUsed( false );
402: // chooser.setFileFilter( new SimpleFileFilter(
403: // NbBundle.getMessage( J2SEClassPathUi.class, "LBL_ZipJarFolderFilter" ), // NOI18N
404: // new String[] {"ZIP","JAR"} ) ); // NOI18N
405: // File curDir = FoldersListSettings.getDefault().getLastUsedClassPathFolder();
406: // chooser.setCurrentDirectory (curDir);
407: // int option = chooser.showOpenDialog( SwingUtilities.getWindowAncestor( list ) ); // Sow the chooser
408: //
409: // if ( option == JFileChooser.APPROVE_OPTION ) {
410: //
411: // File files[] = chooser.getSelectedFiles();
412: // int[] newSelection = ClassPathUiSupport.addJarFiles( listModel, list.getSelectedIndices(), files );
413: // list.setSelectedIndices( newSelection );
414: // curDir = FileUtil.normalizeFile(chooser.getCurrentDirectory());
415: // FoldersListSettings.getDefault().setLastUsedClassPathFolder(curDir);
416: // }
417: // }
418: // else if ( source == addLibrary ) {
419: // Set/*<Library>*/includedLibraries = new HashSet ();
420: // for (int i=0; i< listModel.getSize(); i++) {
421: // ClassPathSupport.Item item = (ClassPathSupport.Item) listModel.get(i);
422: // if (item.getType() == ClassPathSupport.Item.TYPE_LIBRARY && !item.isBroken() ) {
423: // includedLibraries.add( item.getLibrary() );
424: // }
425: // }
426: // Object[] options = new Object[] {
427: // new JButton (NbBundle.getMessage (J2SEClassPathUi.class,"LBL_AddLibrary")),
428: // DialogDescriptor.CANCEL_OPTION
429: // };
430: // ((JButton)options[0]).setEnabled(false);
431: // ((JButton)options[0]).getAccessibleContext().setAccessibleDescription (NbBundle.getMessage (J2SEClassPathUi.class,"AD_AddLibrary"));
432: // LibrariesChooser panel = new LibrariesChooser ((JButton)options[0], includedLibraries);
433: // DialogDescriptor desc = new DialogDescriptor(panel,NbBundle.getMessage( J2SEClassPathUi.class, "LBL_CustomizeCompile_Classpath_AddLibrary" ),
434: // true, options, options[0], DialogDescriptor.DEFAULT_ALIGN,null,null);
435: // Dialog dlg = DialogDisplayer.getDefault().createDialog(desc);
436: // dlg.setVisible(true);
437: // if (desc.getValue() == options[0]) {
438: // int[] newSelection = ClassPathUiSupport.addLibraries( listModel, list.getSelectedIndices(), panel.getSelectedLibraries(), includedLibraries );
439: // list.setSelectedIndices( newSelection );
440: // }
441: // dlg.dispose();
442: // }
443: // else if ( source == addAntArtifact ) {
444: // AntArtifactChooser.ArtifactItem artifactItems[] = AntArtifactChooser.showDialog(
445: // new String[] { JavaProjectConstants.ARTIFACT_TYPE_JAR, JavaProjectConstants.ARTIFACT_TYPE_FOLDER},
446: // project, list.getParent() );
447: // if (artifactItems != null) {
448: // int[] newSelection = ClassPathUiSupport.addArtifacts( listModel, list.getSelectedIndices(), artifactItems);
449: // list.setSelectedIndices( newSelection );
450: // }
451: // }
452: // else if ( source == remove ) {
453: // int[] newSelection = ClassPathUiSupport.remove( listModel, list.getSelectedIndices() );
454: // list.setSelectedIndices( newSelection );
455: // }
456: // else if ( source == moveUp ) {
457: // int[] newSelection = ClassPathUiSupport.moveUp( listModel, list.getSelectedIndices() );
458: // list.setSelectedIndices( newSelection );
459: // }
460: // else if ( source == moveDown ) {
461: // int[] newSelection = ClassPathUiSupport.moveDown( listModel, list.getSelectedIndices() );
462: // list.setSelectedIndices( newSelection );
463: // }
464: // }
465: //
466: //
467: // /** Handles changes in the selection
468: // */
469: // public void valueChanged( ListSelectionEvent e ) {
470: //
471: // // remove enabled only if selection is not empty
472: // boolean canRemove = false;
473: // // and when the selection does not contain unremovable item
474: // if ( selectionModel.getMinSelectionIndex() != -1 ) {
475: // canRemove = true;
476: // int iMin = selectionModel.getMinSelectionIndex();
477: // int iMax = selectionModel.getMinSelectionIndex();
478: // for ( int i = iMin; i <= iMax; i++ ) {
479: //
480: // if ( selectionModel.isSelectedIndex( i ) ) {
481: // ClassPathSupport.Item item = (ClassPathSupport.Item)listModel.get( i );
482: // if ( item.getType() == ClassPathSupport.Item.TYPE_CLASSPATH ) {
483: // canRemove = false;
484: // break;
485: // }
486: // }
487: // }
488: // }
489: //
490: // // addJar allways enabled
491: // // addLibrary allways enabled
492: // // addArtifact allways enabled
493: // // editButton.setEnabled( edit );
494: // remove.setEnabled( canRemove );
495: // moveUp.setEnabled( ClassPathUiSupport.canMoveUp( selectionModel ) );
496: // moveDown.setEnabled( ClassPathUiSupport.canMoveDown( selectionModel, listModel.getSize() ) );
497: //
498: // }
499: // }
500: //
501: // private static class SimpleFileFilter extends FileFilter {
502: //
503: // private String description;
504: // private Collection extensions;
505: //
506: //
507: // public SimpleFileFilter (String description, String[] extensions) {
508: // this.description = description;
509: // this.extensions = Arrays.asList(extensions);
510: // }
511: //
512: // public boolean accept(File f) {
513: // if (f.isDirectory())
514: // return true;
515: // String name = f.getName();
516: // int index = name.lastIndexOf('.'); //NOI18N
517: // if (index <= 0 || index==name.length()-1)
518: // return false;
519: // String extension = name.substring (index+1).toUpperCase();
520: // return this.extensions.contains(extension);
521: // }
522: //
523: // public String getDescription() {
524: // return this.description;
525: // }
526: // }
527:
528: }
|