blob: 81099a210ec5dd6f536eaceda2c32a01abbf6d33 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.j2ee.core.support.java.method;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.lang.model.element.TypeElement;
import javax.swing.AbstractListModel;
import javax.swing.ComboBoxEditor;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JSeparator;
import javax.swing.ListCellRenderer;
import javax.swing.MutableComboBoxModel;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
import org.netbeans.api.java.source.ClassIndex.NameKind;
import org.netbeans.api.java.source.ClassIndex.SearchScope;
import org.netbeans.api.java.source.ClasspathInfo;
import org.netbeans.api.java.source.ElementHandle;
import org.netbeans.api.java.source.ui.TypeElementFinder;
import org.openide.util.NbBundle;
/**
*
* ReturnTypeUIHelper populates and manages the content of the combobox for a return type of method
*
* @author Martin Adamek
*/
public final class ReturnTypeUIHelper {
private static final List<String> LVALUE_TYPES = Arrays.asList(new String[] {
"boolean", "int", "char", "byte", "short", "long", "float", "double", // NOI18N
"Object", "String" // NOI18N
});
private static final class Separator extends JSeparator {
Separator() {
setPreferredSize(new Dimension(getWidth(), 1));
setForeground(Color.BLACK);
}
}
static final Separator SEPARATOR_ITEM = new Separator();
static final Object NEW_ITEM = new Object() {
@Override
public String toString() {
return NbBundle.getMessage(ReturnTypeUIHelper.class, "LBL_Choose"); // NOI18N
}
};
private static class ReturnTypeComboBoxModel extends AbstractListModel implements MutableComboBoxModel {
private List<Object> items;
private Object selectedItem;
private List<String> returnTypes;
private Object previousItem;
private ReturnTypeComboBoxModel(List<String> returnTypes, List<Object> items) {
this.returnTypes = returnTypes;
this.items = items;
}
public void setSelectedItem(Object anItem) {
if (selectedItem == null || !selectedItem.equals(anItem)) {
previousItem = selectedItem;
selectedItem = anItem;
fireContentsChanged(this, 0, -1);
}
}
public Object getSelectedItem() {
return selectedItem;
}
public Object getElementAt(int index) {
return items.get(index);
}
public int getSize() {
return items.size();
}
Object getPreviousItem() {
return previousItem;
}
List<String> getReturnTypes() {
return returnTypes;
}
public void addElement(Object elem) {
items.add(elem);
}
public void removeElement(Object elem) {
items.remove(elem);
}
public void insertElementAt(Object elem, int index) {
items.set(index, elem);
}
public void removeElementAt(int index) {
items.remove(index);
}
}
/**
* Get data source list cell renderer.
* @return data source list cell renderer instance.
* @since 1.16
*/
public static ListCellRenderer createDatasourceListCellRenderer() {
return new ReturnTypeListCellRenderer();
}
private static class ReturnTypeListCellRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
if (value == SEPARATOR_ITEM) {
return SEPARATOR_ITEM;
}
else {
setText(value != null ? value.toString() : ""); // NOI18N
setToolTipText(""); // NOI18N
}
return this;
}
}
private static class ReturnTypeComboBoxEditor implements ComboBoxEditor {
private ComboBoxEditor delegate;
private Object oldValue;
ReturnTypeComboBoxEditor(ComboBoxEditor delegate) {
this.delegate = delegate;
}
public Component getEditorComponent() {
return delegate.getEditorComponent();
}
public void setItem(Object anObject) {
JTextComponent editor = getEditor();
if (anObject != null) {
String text = anObject.toString();
editor.setText(text);
oldValue = anObject;
}
else {
editor.setText("");
}
}
// this method is taken from javax.swing.plaf.basic.BasicComboBoxEditor
public Object getItem() {
JTextComponent editor = getEditor();
Object newValue = editor.getText();
if (oldValue != null && !(oldValue instanceof String)) {
// The original value is not a string. Should return the value in it's
// original type.
if (newValue.equals(oldValue.toString())) {
return oldValue;
} else {
// Must take the value from the editor and get the value and cast it to the new type.
Class<?> cls = oldValue.getClass();
try {
Method method = cls.getMethod("valueOf", String.class); // NOI18N
newValue = method.invoke(oldValue, new Object[] { editor.getText() });
} catch (Exception ex) {
// Fail silently and return the newValue (a String object)
Logger.getLogger("ReturnTypeUIHelper").log(Level.FINE, "ignored excep[tion", ex); //NOI18N
}
}
}
return newValue;
}
public void selectAll() {
delegate.selectAll();
}
public void addActionListener(ActionListener l) {
delegate.addActionListener(l);
}
public void removeActionListener(ActionListener l) {
delegate.removeActionListener(l);
}
private JTextComponent getEditor() {
Component comp = getEditorComponent();
assert (comp instanceof JTextComponent);
return (JTextComponent)comp;
}
}
private ReturnTypeUIHelper() {
}
/**
* Entry point for the combobox initialization. It connects combobox with its content and
* add items for the combobox content management.
*
* @param provider Java EE module provider.
* @param combo combobox to manage.
*/
public static void connect(JComboBox combo, ClasspathInfo cpInfo) {
connect(combo, null, cpInfo);
}
private static final void connect(final JComboBox combo, final String selectedType, final ClasspathInfo cpInfo) {
combo.setEditable(true);
combo.setEditor(new ReturnTypeComboBoxEditor(combo.getEditor()));
combo.setRenderer(new ReturnTypeListCellRenderer());
populate(LVALUE_TYPES, true, combo, selectedType, false);
Component toListenOn = (combo.isEditable() ? combo.getEditor().getEditorComponent() : combo);
toListenOn.addKeyListener(new KeyAdapter() {
public void keyPressed(final KeyEvent e) {
int keyCode = e.getKeyCode();
if (KeyEvent.VK_ENTER == keyCode) {
Object selectedItem = combo.getSelectedItem();
if (selectedItem == NEW_ITEM) {
performBrowseType(combo, cpInfo);
e.consume();
}
}
}
});
combo.addActionListener(new ActionListener() {
Object previousItem;
int previousIndex = combo.getSelectedIndex();
public void actionPerformed(ActionEvent e) {
Object selectedItem = combo.getSelectedItem();
// skipping of separator
if (selectedItem == SEPARATOR_ITEM) {
int selectedIndex = combo.getSelectedIndex();
if (selectedIndex > previousIndex) {
previousIndex = selectedIndex + 1;
previousItem = combo.getItemAt(previousIndex);
} else {
previousIndex = selectedIndex - 1;
previousItem = combo.getItemAt(previousIndex);
}
combo.setSelectedItem(previousItem);
// handling mouse click, see KeyEvent.getKeyModifiersText(e.getModifiers())
} else if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
if (selectedItem == NEW_ITEM) {
performBrowseType(combo, cpInfo);
}
}
}
});
}
private static void performBrowseType(final JComboBox combo, final ClasspathInfo cpInfo) {
final ReturnTypeComboBoxModel model = (ReturnTypeComboBoxModel) combo.getModel();
combo.setPopupVisible(false);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final ElementHandle<TypeElement> handle = TypeElementFinder.find(cpInfo, new TypeElementFinder.Customizer() {
public Set<ElementHandle<TypeElement>> query(ClasspathInfo classpathInfo, String textForQuery, NameKind nameKind, Set<SearchScope> searchScopes) {//GEN-LAST:event_browseButtonActionPerformed
return classpathInfo.getClassIndex().getDeclaredTypes(textForQuery, nameKind, searchScopes);
}
public boolean accept(ElementHandle<TypeElement> typeHandle) {
return true;
}
});
combo.setPopupVisible(false);
if (handle == null) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setSelectedItem(combo, model.getPreviousItem());
}
});
} else {
setSelectedItem(combo, handle.getQualifiedName());
}
}
});
}
private static List populate(List<String> types, boolean creationSupported, final JComboBox combo, final String selectedType, boolean selectItemLater) {
List<Object> items = (types == null ? new LinkedList<Object>() : new LinkedList<Object>(types));
if (items.size() > 0) {
items.add(SEPARATOR_ITEM);
}
if (creationSupported) {
items.add(NEW_ITEM);
}
ReturnTypeComboBoxModel model = new ReturnTypeComboBoxModel(types, items);
combo.setModel(model);
if (selectedType != null) {
// Ensure that the correct item is selected before listeners like FocusListener are called.
// ActionListener.actionPerformed() is not called if this method is already called from
// actionPerformed(), in that case selectItemLater should be set to true and setSelectedItem()
// below is called asynchronously so that the actionPerformed() is called
setSelectedItem(combo, selectedType);
if (selectItemLater) {
SwingUtilities.invokeLater(new Runnable() { // postpone item selection to enable event firing from JCombobox.setSelectedItem()
public void run() {
setSelectedItem(combo, selectedType);
// populate(LVALUE_TYPES, true, combo, "Object", false);
}
});
}
}
return types;
}
private static void setSelectedItem(final JComboBox combo, final Object item) {
combo.setSelectedItem(item);
if (combo.isEditable() && combo.getEditor() != null) {
// item must be set in the editor in case of editable combobox
combo.configureEditor(combo.getEditor(), combo.getSelectedItem());
}
}
}