/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
* Microsystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.netbeans.modules.ruby.javaint;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.BeanInfo;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.ButtonModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JList;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import org.netbeans.api.project.Project;
import org.netbeans.modules.ruby.rubyproject.ProjectPropertyExtender;
import org.netbeans.modules.ruby.rubyproject.ui.FoldersListSettings;
import org.netbeans.modules.ruby.spi.project.support.rake.PropertyEvaluator;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
/** Classes containing code speciic for handling UI of J2SE project classpath
*
* @author Petr Hrebejk
*/
public class RubyClassPathUi {
private RubyClassPathUi() {}
// Innerclasses ------------------------------------------------------------
/** Renderer which can be used to render the classpath in lists
*/
public static class ClassPathListCellRenderer extends DefaultListCellRenderer {
private static final Pattern FOREIGN_PLAIN_FILE_REFERENCE = Pattern.compile("\\$\\{file\\.reference\\.([^${}]+)\\}"); // NOI18N
private static final Pattern UNKNOWN_FILE_REFERENCE = Pattern.compile("\\$\\{([^${}]+)\\}"); // NOI18N
private static String RESOURCE_ICON_JAR = "org/netbeans/modules/ruby/javaint//jar.gif"; //NOI18N
private static String RESOURCE_ICON_LIBRARY = "org/netbeans/modules/ruby/javaint/libraries.gif"; //NOI18N
private static String RESOURCE_ICON_ARTIFACT = "org/netbeans/modules/ruby/javaint//projectDependencies.gif"; //NOI18N
private static String RESOURCE_ICON_CLASSPATH = "org/netbeans/modules/ruby/javaint//referencedClasspath.gif"; //NOI18N
private static String RESOURCE_ICON_BROKEN_BADGE = "org/netbeans/modules/ruby/javaint//brokenProjectBadge.gif"; //NOI18N
private static ImageIcon ICON_JAR = ImageUtilities.loadImageIcon(RESOURCE_ICON_JAR, false);
private static ImageIcon ICON_FOLDER = null;
private static ImageIcon ICON_LIBRARY = ImageUtilities.loadImageIcon(RESOURCE_ICON_LIBRARY, false);
private static ImageIcon ICON_ARTIFACT = ImageUtilities.loadImageIcon(RESOURCE_ICON_ARTIFACT, false);
private static ImageIcon ICON_CLASSPATH = ImageUtilities.loadImageIcon(RESOURCE_ICON_CLASSPATH, false);
private static ImageIcon ICON_BROKEN_BADGE = ImageUtilities.loadImageIcon(RESOURCE_ICON_BROKEN_BADGE, false);
private static ImageIcon ICON_BROKEN_JAR;
private static ImageIcon ICON_BROKEN_LIBRARY;
private static ImageIcon ICON_BROKEN_ARTIFACT;
private PropertyEvaluator evaluator;
// Contains well known paths in the RubyProject
private static final Map WELL_KNOWN_PATHS_NAMES = new HashMap();
static {
// WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.JAVAC_CLASSPATH, NbBundle.getMessage( RubyProjectProperties.class, "LBL_JavacClasspath_DisplayName" ) );
// WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.JAVAC_TEST_CLASSPATH, NbBundle.getMessage( RubyProjectProperties.class,"LBL_JavacTestClasspath_DisplayName") );
// WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.RUN_CLASSPATH, NbBundle.getMessage( RubyProjectProperties.class, "LBL_RunClasspath_DisplayName" ) );
// WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.RUN_TEST_CLASSPATH, NbBundle.getMessage( RubyProjectProperties.class, "LBL_RunTestClasspath_DisplayName" ) );
// WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.BUILD_CLASSES_DIR, NbBundle.getMessage( RubyProjectProperties.class, "LBL_BuildClassesDir_DisplayName" ) );
// WELL_KNOWN_PATHS_NAMES.put( RubyProjectProperties.BUILD_TEST_CLASSES_DIR, NbBundle.getMessage (RubyProjectProperties.class,"LBL_BuildTestClassesDir_DisplayName") );
};
public ClassPathListCellRenderer( PropertyEvaluator evaluator ) {
super();
this.evaluator = evaluator;
}
public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
ProjectPropertyExtender.Item item = (ProjectPropertyExtender.Item)value;
super.getListCellRendererComponent( list, getDisplayName( item ), index, isSelected, cellHasFocus );
setIcon( getIcon( item ) );
setToolTipText( getToolTipText( item ) );
return this;
}
private String getDisplayName( ProjectPropertyExtender.Item item ) {
switch ( item.getType() ) {
// case ProjectPropertyExtender.Item.TYPE_LIBRARY:
// if ( item.isBroken() ) {
// return NbBundle.getMessage( RubyClassPathUi.class, "LBL_MISSING_LIBRARY", getLibraryName( item ) );
// }
// else {
// return item.getLibrary().getDisplayName();
// }
// case ProjectPropertyExtender.Item.TYPE_CLASSPATH:
// String name = (String)WELL_KNOWN_PATHS_NAMES.get( ClassPathSupport.getAntPropertyName( item.getReference() ) );
// return name == null ? item.getReference() : name;
// case ProjectPropertyExtender.Item.TYPE_ARTIFACT:
// if ( item.isBroken() ) {
// return NbBundle.getMessage( RubyClassPathUi.class, "LBL_MISSING_PROJECT", getProjectName( item ) );
// }
// else {
// Project p = item.getArtifact().getProject();
// ProjectInformation pi = ProjectUtils.getInformation(p);
// String projectName = pi.getDisplayName();
// return MessageFormat.format (NbBundle.getMessage(RubyClassPathUi.class,"MSG_ProjectArtifactFormat"), new Object[] {
// projectName,
// item.getArtifactURI().toString()
// });
// }
case ProjectPropertyExtender.Item.TYPE_JAR:
if ( item.isBroken() ) {
return NbBundle.getMessage( RubyClassPathUi.class, "LBL_MISSING_FILE", getFileRefName( item ) );
}
else {
return item.getFile().getPath();
}
}
return item.getReference(); // XXX
}
static Icon getIcon( ProjectPropertyExtender.Item item ) {
switch ( item.getType() ) {
case ProjectPropertyExtender.Item.TYPE_LIBRARY:
if ( item.isBroken() ) {
if ( ICON_BROKEN_LIBRARY == null ) {
ICON_BROKEN_LIBRARY = new ImageIcon( ImageUtilities.mergeImages( ICON_LIBRARY.getImage(), ICON_BROKEN_BADGE.getImage(), 7, 7 ) );
}
return ICON_BROKEN_LIBRARY;
}
else {
return ICON_LIBRARY;
}
// case ProjectPropertyExtender.Item.TYPE_ARTIFACT:
// if ( item.isBroken() ) {
// if ( ICON_BROKEN_ARTIFACT == null ) {
// ICON_BROKEN_ARTIFACT = new ImageIcon( ImageUtilities.mergeImages( ICON_ARTIFACT.getImage(), ICON_BROKEN_BADGE.getImage(), 7, 7 ) );
// }
// return ICON_BROKEN_ARTIFACT;
// }
// else {
// Project p = item.getArtifact().getProject();
// if (p != null) {
// ProjectInformation pi = ProjectUtils.getInformation(p);
// return pi.getIcon();
// }
// return ICON_ARTIFACT;
// }
case ProjectPropertyExtender.Item.TYPE_JAR:
if ( item.isBroken() ) {
if ( ICON_BROKEN_JAR == null ) {
ICON_BROKEN_JAR = new ImageIcon( ImageUtilities.mergeImages( ICON_JAR.getImage(), ICON_BROKEN_BADGE.getImage(), 7, 7 ) );
}
return ICON_BROKEN_JAR;
}
else {
File file = item.getFile();
return file.isDirectory() ? getFolderIcon() : ICON_JAR;
}
case ProjectPropertyExtender.Item.TYPE_CLASSPATH:
return ICON_CLASSPATH;
}
return null; // XXX
}
private String getToolTipText( ProjectPropertyExtender.Item item ) {
if ( item.isBroken() &&
( item.getType() == ProjectPropertyExtender.Item.TYPE_JAR ||
item.getType() == ProjectPropertyExtender.Item.TYPE_ARTIFACT ) ) {
return evaluator.evaluate( item.getReference() );
}
return getDisplayName( item ); // XXX
}
private static ImageIcon getFolderIcon() {
if ( ICON_FOLDER == null ) {
DataFolder dataFolder = DataFolder.findFolder( FileUtil.getConfigRoot() );
ICON_FOLDER = new ImageIcon( dataFolder.getNodeDelegate().getIcon( BeanInfo.ICON_COLOR_16x16 ) );
}
return ICON_FOLDER;
}
private String getProjectName( ProjectPropertyExtender.Item item ) {
String ID = item.getReference();
// something in the form of "${reference.project-name.id}"
return ID.substring(12, ID.indexOf(".", 12)); // NOI18N
}
private String getLibraryName( ProjectPropertyExtender.Item item ) {
String ID = item.getReference();
// something in the form of "${libs.junit.classpath}"
return ID.substring(7, ID.indexOf(".classpath")); // NOI18N
}
private String getFileRefName( ProjectPropertyExtender.Item item ) {
String ID = item.getReference();
// something in the form of "${file.reference.smth.jar}"
Matcher m = FOREIGN_PLAIN_FILE_REFERENCE.matcher(ID);
if (m.matches()) {
return m.group(1);
}
m = UNKNOWN_FILE_REFERENCE.matcher(ID);
if (m.matches()) {
return m.group(1);
}
return ID;
}
}
public static class EditMediator implements ActionListener, ListSelectionListener {
private final Project project;
private final JList list;
private final DefaultListModel listModel;
private final ListSelectionModel selectionModel;
private final ButtonModel addJar;
private final ButtonModel addLibrary;
private final ButtonModel addAntArtifact;
private final ButtonModel remove;
private final ButtonModel moveUp;
private final ButtonModel moveDown;
public EditMediator( Project project,
JList list,
DefaultListModel listModel,
ButtonModel addJar,
ButtonModel addLibrary,
ButtonModel addAntArtifact,
ButtonModel remove,
ButtonModel moveUp,
ButtonModel moveDown ) {
// Remember all buttons
this.list = list;
if ( !( list.getModel() instanceof DefaultListModel ) ) {
throw new IllegalArgumentException( "The list's model has to be of class DefaultListModel" ); // NOI18N
}
this.listModel = (DefaultListModel)list.getModel();
this.selectionModel = list.getSelectionModel();
this.addJar = addJar;
this.addLibrary = addLibrary;
this.addAntArtifact = addAntArtifact;
this.remove = remove;
this.moveUp = moveUp;
this.moveDown = moveDown;
this.project = project;
}
public static void register(Project project,
JList list,
DefaultListModel listModel,
ButtonModel addJar,
ButtonModel addLibrary,
ButtonModel addAntArtifact,
ButtonModel remove,
ButtonModel moveUp,
ButtonModel moveDown ) {
EditMediator em = new EditMediator( project,
list,
listModel,
addJar,
addLibrary,
addAntArtifact,
remove,
moveUp,
moveDown );
// Register the listener on all buttons
addJar.addActionListener( em );
addLibrary.addActionListener( em );
addAntArtifact.addActionListener( em );
remove.addActionListener( em );
moveUp.addActionListener( em );
moveDown.addActionListener( em );
// On list selection
em.selectionModel.addListSelectionListener( em );
// Set the initial state of the buttons
em.valueChanged( null );
}
// Implementation of ActionListener ------------------------------------
/** Handles button events
*/
public void actionPerformed( ActionEvent e ) {
Object source = e.getSource();
if ( source == addJar ) {
// Let user search for the Jar file
JFileChooser chooser = new JFileChooser();
FileUtil.preventFileChooserSymlinkTraversal(chooser, null);
chooser.setFileSelectionMode( JFileChooser.FILES_AND_DIRECTORIES );
chooser.setMultiSelectionEnabled( true );
chooser.setDialogTitle( NbBundle.getMessage( RubyClassPathUi.class, "LBL_AddJar_DialogTitle" ) ); // NOI18N
//#61789 on old macosx (jdk 1.4.1) these two method need to be called in this order.
chooser.setAcceptAllFileFilterUsed( false );
chooser.setFileFilter( new SimpleFileFilter(
NbBundle.getMessage( RubyClassPathUi.class, "LBL_ZipJarFolderFilter" ), // NOI18N
new String[] {"ZIP","JAR"} ) ); // NOI18N
File curDir = FoldersListSettings.getDefault().getLastUsedClassPathFolder();
chooser.setCurrentDirectory (curDir);
int option = chooser.showOpenDialog( SwingUtilities.getWindowAncestor( list ) ); // Sow the chooser
if ( option == JFileChooser.APPROVE_OPTION ) {
File files[] = chooser.getSelectedFiles();
int[] newSelection = ClassPathUiSupport.addJarFiles( listModel, list.getSelectedIndices(), files );
list.setSelectedIndices( newSelection );
curDir = FileUtil.normalizeFile(chooser.getCurrentDirectory());
FoldersListSettings.getDefault().setLastUsedClassPathFolder(curDir);
}
}
// else if ( source == addLibrary ) {
// Set/*<Library>*/includedLibraries = new HashSet ();
// for (int i=0; i< listModel.getSize(); i++) {
// ProjectPropertyExtender.Item item = (ProjectPropertyExtender.Item) listModel.get(i);
// if (item.getType() == ProjectPropertyExtender.Item.TYPE_LIBRARY && !item.isBroken() ) {
// includedLibraries.add( item.getLibrary() );
// }
// }
// Object[] options = new Object[] {
// new JButton (NbBundle.getMessage (RubyClassPathUi.class,"LBL_AddLibrary")),
// DialogDescriptor.CANCEL_OPTION
// };
// ((JButton)options[0]).setEnabled(false);
// ((JButton)options[0]).getAccessibleContext().setAccessibleDescription (NbBundle.getMessage (RubyClassPathUi.class,"AD_AddLibrary"));
// LibrariesChooser panel = new LibrariesChooser ((JButton)options[0], includedLibraries);
// DialogDescriptor desc = new DialogDescriptor(panel,NbBundle.getMessage( RubyClassPathUi.class, "LBL_CustomizeCompile_Classpath_AddLibrary" ),
// true, options, options[0], DialogDescriptor.DEFAULT_ALIGN,null,null);
// Dialog dlg = DialogDisplayer.getDefault().createDialog(desc);
// dlg.setVisible(true);
// if (desc.getValue() == options[0]) {
// int[] newSelection = ClassPathUiSupport.addLibraries( listModel, list.getSelectedIndices(), panel.getSelectedLibraries(), includedLibraries );
// list.setSelectedIndices( newSelection );
// }
// dlg.dispose();
//// }
//// else if ( source == addAntArtifact ) {
//// AntArtifactChooser.ArtifactItem artifactItems[] = AntArtifactChooser.showDialog(
//// new String[] { JavaProjectConstants.ARTIFACT_TYPE_JAR, JavaProjectConstants.ARTIFACT_TYPE_FOLDER},
//// project, list.getParent() );
//// if (artifactItems != null) {
//// int[] newSelection = ClassPathUiSupport.addArtifacts( listModel, list.getSelectedIndices(), artifactItems);
//// list.setSelectedIndices( newSelection );
//// }
// }
else if ( source == remove ) {
int[] newSelection = ClassPathUiSupport.remove( listModel, list.getSelectedIndices() );
list.setSelectedIndices( newSelection );
}
else if ( source == moveUp ) {
int[] newSelection = ClassPathUiSupport.moveUp( listModel, list.getSelectedIndices() );
list.setSelectedIndices( newSelection );
}
else if ( source == moveDown ) {
int[] newSelection = ClassPathUiSupport.moveDown( listModel, list.getSelectedIndices() );
list.setSelectedIndices( newSelection );
}
}
/** Handles changes in the selection
*/
public void valueChanged( ListSelectionEvent e ) {
// remove enabled only if selection is not empty
boolean canRemove = false;
// and when the selection does not contain unremovable item
if ( selectionModel.getMinSelectionIndex() != -1 ) {
canRemove = true;
int iMin = selectionModel.getMinSelectionIndex();
int iMax = selectionModel.getMinSelectionIndex();
for ( int i = iMin; i <= iMax; i++ ) {
if ( selectionModel.isSelectedIndex( i ) ) {
ProjectPropertyExtender.Item item = (ProjectPropertyExtender.Item)listModel.get( i );
if ( item.getType() == ProjectPropertyExtender.Item.TYPE_CLASSPATH ) {
canRemove = false;
break;
}
}
}
}
// addJar allways enabled
// addLibrary allways enabled
// addArtifact allways enabled
// editButton.setEnabled( edit );
remove.setEnabled( canRemove );
moveUp.setEnabled( ClassPathUiSupport.canMoveUp( selectionModel ) );
moveDown.setEnabled( ClassPathUiSupport.canMoveDown( selectionModel, listModel.getSize() ) );
}
}
private static class SimpleFileFilter extends FileFilter {
private String description;
private Collection extensions;
public SimpleFileFilter (String description, String[] extensions) {
this.description = description;
this.extensions = Arrays.asList(extensions);
}
public boolean accept(File f) {
if (f.isDirectory())
return true;
String name = f.getName();
int index = name.lastIndexOf('.'); //NOI18N
if (index <= 0 || index==name.length()-1)
return false;
String extension = name.substring (index+1).toUpperCase();
return this.extensions.contains(extension);
}
public String getDescription() {
return this.description;
}
}
}